[6.x] [APM] Split API into transactions and transaction_groups (#26349) | [APM] Sort by score to get transaction samples with sampled:true (#26389) | [APM] Move impact calculation to Elasticsearch (#26436) (#26581)

* [APM] Split API into transactions and transaction_groups (#26349)

* [APM] Sort by score to get transaction samples with sampled:true (#26389)

* [APM] Move impact calculation to Elasticsearch (#26436)

* [APM] Move impact calculation to Elasticsearch

* Renamed “durationSum” to “sum” and went back to single, mutable impact
This commit is contained in:
Søren Louv-Jansen 2018-12-04 01:10:44 +01:00 committed by GitHub
parent 7ca581c4a9
commit 43183614b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 637 additions and 469 deletions

View file

@ -6,6 +6,7 @@
import { resolve } from 'path';
import { initTransactionsApi } from './server/routes/transactions';
import { initTransactionGroupsApi } from './server/routes/transaction_groups';
import { initServicesApi } from './server/routes/services';
import { initErrorsApi } from './server/routes/errors';
import { initStatusApi } from './server/routes/status_check';
@ -64,6 +65,7 @@ export function apm(kibana) {
init(server) {
initTransactionsApi(server);
initTransactionGroupsApi(server);
initTracesApi(server);
initServicesApi(server);
initErrorsApi(server);

View file

@ -8,9 +8,8 @@ import React from 'react';
import { shallow } from 'enzyme';
import { ServiceOverview } from '../view';
import { STATUS } from '../../../../constants';
import * as apmRestServices from '../../../../services/rest/apm';
jest.mock('../../../../services/rest/apm');
import * as apmRestServices from '../../../../services/rest/apm/status_check';
jest.mock('../../../../services/rest/apm/status_check');
describe('Service Overview -> View', () => {
let mockAgentStatus;
@ -22,7 +21,6 @@ describe('Service Overview -> View', () => {
dataFound: true
};
// eslint-disable-next-line import/namespace
apmRestServices.loadAgentStatus = jest.fn(() =>
Promise.resolve(mockAgentStatus)
);

View file

@ -9,7 +9,7 @@ import React, { Component } from 'react';
import { RRRRenderResponse } from 'react-redux-request';
import { IUrlParams } from 'x-pack/plugins/apm/public/store/urlParams';
import { IServiceListItem } from 'x-pack/plugins/apm/server/lib/services/get_services';
import { loadAgentStatus } from '../../../services/rest/apm';
import { loadAgentStatus } from '../../../services/rest/apm/status_check';
import { ServiceListRequest } from '../../../store/reactReduxRequest/serviceList';
import { EmptyMessage } from '../../shared/EmptyMessage';
import { SetupInstructionsLink } from '../../shared/SetupInstructionsLink';

View file

@ -1,314 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// @ts-ignore
import { camelizeKeys } from 'humps';
import { ServiceAPIResponse } from 'x-pack/plugins/apm/server/lib/services/get_service';
import { ServiceListAPIResponse } from 'x-pack/plugins/apm/server/lib/services/get_services';
import { TraceListAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_top_traces';
import { TraceAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_trace';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { ITransactionDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/distribution';
import { TransactionListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_top_transactions';
import { TransactionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_transaction';
import { SpanListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/spans/get_spans';
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { IUrlParams } from '../../store/urlParams';
// @ts-ignore
import { convertKueryToEsQuery } from '../kuery';
import { callApi } from './callApi';
// @ts-ignore
import { getAPMIndexPattern } from './savedObjects';
export async function loadLicense() {
return callApi({
pathname: `/api/xpack/v1/info`
});
}
export async function loadServerStatus() {
return callApi({
pathname: `/api/apm/status/server`
});
}
export async function loadAgentStatus() {
return callApi<{ dataFound: boolean }>({
pathname: `/api/apm/status/agent`
});
}
export async function getEncodedEsQuery(kuery?: string) {
if (!kuery) {
return;
}
const indexPattern = await getAPMIndexPattern();
if (!indexPattern) {
return;
}
const esFilterQuery = convertKueryToEsQuery(kuery, indexPattern);
return encodeURIComponent(JSON.stringify(esFilterQuery));
}
export async function loadServiceList({ start, end, kuery }: IUrlParams) {
return callApi<ServiceListAPIResponse>({
pathname: `/api/apm/services`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadServiceDetails({
serviceName,
start,
end,
kuery
}: IUrlParams) {
return callApi<ServiceAPIResponse>({
pathname: `/api/apm/services/${serviceName}`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadTraceList({ start, end, kuery }: IUrlParams) {
const groups = await callApi<TraceListAPIResponse>({
pathname: '/api/apm/traces',
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
return groups.map(group => {
group.sample = addVersion(group.sample);
return group;
});
}
export async function loadTransactionList({
serviceName,
start,
end,
kuery,
transactionType
}: IUrlParams) {
const groups = await callApi<TransactionListAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transactions`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery),
transaction_type: transactionType
}
});
return groups.map(group => {
group.sample = addVersion(group.sample);
return group;
});
}
export async function loadTransactionDistribution({
serviceName,
start,
end,
transactionName,
transactionId,
kuery
}: IUrlParams) {
return callApi<ITransactionDistributionAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transactions/distribution`,
query: {
start,
end,
transaction_name: transactionName,
transaction_id: transactionId,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
function addVersion<T extends Span | Transaction | null | undefined>(
item: T
): T {
if (item != null) {
item.version = item.hasOwnProperty('trace') ? 'v2' : 'v1';
}
return item;
}
function addSpanId(hit: Span, i: number) {
if (!hit.span.id) {
hit.span.id = i;
}
return hit;
}
export async function loadSpans({
serviceName,
start,
end,
transactionId
}: IUrlParams) {
const hits = await callApi<SpanListAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}/spans`,
query: {
start,
end
}
});
return hits.map(addVersion).map(addSpanId);
}
export async function loadTrace({ traceId, start, end }: IUrlParams) {
const hits = await callApi<TraceAPIResponse>(
{
pathname: `/api/apm/traces/${traceId}`,
query: {
start,
end
}
},
{
camelcase: false
}
);
return hits.map(addVersion);
}
export async function loadTransaction({
serviceName,
start,
end,
transactionId,
traceId,
kuery
}: IUrlParams) {
const result = await callApi<TransactionAPIResponse>(
{
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}`,
query: {
traceId,
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
},
{
camelcase: false
}
);
return addVersion(result);
}
export async function loadCharts({
serviceName,
start,
end,
kuery,
transactionType,
transactionName
}: IUrlParams) {
return callApi<TimeSeriesAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transactions/charts`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery),
transaction_type: transactionType,
transaction_name: transactionName
}
});
}
interface ErrorGroupListParams extends IUrlParams {
size: number;
sortField: string;
sortDirection: string;
}
export async function loadErrorGroupList({
serviceName,
start,
end,
kuery,
size,
sortField,
sortDirection
}: ErrorGroupListParams) {
return callApi({
pathname: `/api/apm/services/${serviceName}/errors`,
query: {
start,
end,
size,
sortField,
sortDirection,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadErrorGroupDetails({
serviceName,
start,
end,
kuery,
errorGroupId
}: IUrlParams) {
// TODO: add types when error section is converted to ts
const res = await callApi<any>(
{
pathname: `/api/apm/services/${serviceName}/errors/${errorGroupId}`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
},
{
camelcase: false
}
);
const camelizedRes: any = camelizeKeys(res);
if (res.error.context) {
camelizedRes.error.context = res.error.context;
}
return camelizedRes;
}
export async function loadErrorDistribution({
serviceName,
start,
end,
kuery,
errorGroupId
}: IUrlParams) {
return callApi({
pathname: `/api/apm/services/${serviceName}/errors/${errorGroupId}/distribution`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
// @ts-ignore
import { convertKueryToEsQuery } from '../../kuery';
// @ts-ignore
import { getAPMIndexPattern } from '../savedObjects';
export async function getEncodedEsQuery(kuery?: string) {
if (!kuery) {
return;
}
const indexPattern = await getAPMIndexPattern();
if (!indexPattern) {
return;
}
const esFilterQuery = convertKueryToEsQuery(kuery, indexPattern);
return encodeURIComponent(JSON.stringify(esFilterQuery));
}
export function addVersion<T extends Span | Transaction | null | undefined>(
item: T
): T {
if (item != null) {
item.version = item.hasOwnProperty('trace') ? 'v2' : 'v1';
}
return item;
}

View file

@ -0,0 +1,83 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { camelizeKeys } from 'humps';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { getEncodedEsQuery } from './apm';
interface ErrorGroupListParams extends IUrlParams {
size: number;
sortField: string;
sortDirection: string;
}
export async function loadErrorGroupList({
serviceName,
start,
end,
kuery,
size,
sortField,
sortDirection
}: ErrorGroupListParams) {
return callApi({
pathname: `/api/apm/services/${serviceName}/errors`,
query: {
start,
end,
size,
sortField,
sortDirection,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadErrorGroupDetails({
serviceName,
start,
end,
kuery,
errorGroupId
}: IUrlParams) {
// TODO: add types when error section is converted to ts
const res = await callApi<any>(
{
pathname: `/api/apm/services/${serviceName}/errors/${errorGroupId}`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
},
{
camelcase: false
}
);
const camelizedRes: any = camelizeKeys(res);
if (res.error.context) {
camelizedRes.error.context = res.error.context;
}
return camelizedRes;
}
export async function loadErrorDistribution({
serviceName,
start,
end,
kuery,
errorGroupId
}: IUrlParams) {
return callApi({
pathname: `/api/apm/services/${serviceName}/errors/${errorGroupId}/distribution`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ServiceAPIResponse } from 'x-pack/plugins/apm/server/lib/services/get_service';
import { ServiceListAPIResponse } from 'x-pack/plugins/apm/server/lib/services/get_services';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { getEncodedEsQuery } from './apm';
export async function loadServiceList({ start, end, kuery }: IUrlParams) {
return callApi<ServiceListAPIResponse>({
pathname: `/api/apm/services`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadServiceDetails({
serviceName,
start,
end,
kuery
}: IUrlParams) {
return callApi<ServiceAPIResponse>({
pathname: `/api/apm/services/${serviceName}`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { callApi } from '../callApi';
export async function loadServerStatus() {
return callApi({
pathname: `/api/apm/status/server`
});
}
export async function loadAgentStatus() {
return callApi<{ dataFound: boolean }>({
pathname: `/api/apm/status/agent`
});
}

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { TraceListAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_top_traces';
import { TraceAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_trace';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { addVersion, getEncodedEsQuery } from './apm';
export async function loadTrace({ traceId, start, end }: IUrlParams) {
const hits = await callApi<TraceAPIResponse>(
{
pathname: `/api/apm/traces/${traceId}`,
query: {
start,
end
}
},
{
camelcase: false
}
);
return hits.map(addVersion);
}
export async function loadTraceList({ start, end, kuery }: IUrlParams) {
const groups = await callApi<TraceListAPIResponse>({
pathname: '/api/apm/traces',
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
return groups.map(group => {
group.sample = addVersion(group.sample);
return group;
});
}

View file

@ -0,0 +1,93 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { ITransactionDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/distribution';
import { TransactionListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_top_transactions';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { addVersion, getEncodedEsQuery } from './apm';
export async function loadTransactionList({
serviceName,
start,
end,
kuery,
transactionType = 'request'
}: IUrlParams) {
const groups = await callApi<TransactionListAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transaction_groups/${transactionType}`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
return groups.map(group => {
group.sample = addVersion(group.sample);
return group;
});
}
export async function loadTransactionDistribution({
serviceName,
start,
end,
transactionName,
transactionType = 'request',
transactionId,
kuery
}: Required<IUrlParams>) {
return callApi<ITransactionDistributionAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transaction_groups/${transactionType}/${encodeURIComponent(
transactionName
)}/distribution`,
query: {
start,
end,
transactionId,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadDetailsCharts({
serviceName,
start,
end,
kuery,
transactionType = 'request',
transactionName
}: Required<IUrlParams>) {
return callApi<TimeSeriesAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transaction_groups/${transactionType}/${encodeURIComponent(
transactionName
)}/charts`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}
export async function loadOverviewCharts({
serviceName,
start,
end,
kuery,
transactionType = 'request'
}: IUrlParams) {
return callApi<TimeSeriesAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transaction_groups/${transactionType}/charts`,
query: {
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});
}

View file

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { TransactionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_transaction';
import { SpanListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/spans/get_spans';
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { addVersion, getEncodedEsQuery } from './apm';
export async function loadSpans({
serviceName,
start,
end,
transactionId
}: IUrlParams) {
const hits = await callApi<SpanListAPIResponse>({
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}/spans`,
query: {
start,
end
}
});
return hits.map(addVersion).map(addSpanId);
}
function addSpanId(hit: Span, i: number) {
if (!hit.span.id) {
hit.span.id = i;
}
return hit;
}
export async function loadTransaction({
serviceName,
start,
end,
transactionId,
traceId,
kuery
}: IUrlParams) {
const result = await callApi<TransactionAPIResponse>(
{
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}`,
query: {
traceId,
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
},
{
camelcase: false
}
);
return addVersion(result);
}

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { callApi } from './callApi';
export async function loadLicense() {
return callApi({
pathname: `/api/xpack/v1/info`
});
}

View file

@ -5,7 +5,7 @@
*/
import React from 'react';
import * as rest from '../../../services/rest/apm';
import * as rest from '../../../services/rest/apm/services';
import { getServiceList, ServiceListRequest } from '../serviceList';
import { mountWithStore } from '../../../utils/testHelpers';

View file

@ -6,7 +6,7 @@
import React from 'react';
import { Request } from 'react-redux-request';
import { loadErrorDistribution } from '../../services/rest/apm';
import { loadErrorDistribution } from '../../services/rest/apm/error_groups';
import { createInitialDataSelector } from './helpers';
const ID = 'errorDistribution';

View file

@ -7,7 +7,7 @@
import React from 'react';
import { createInitialDataSelector } from './helpers';
import { Request } from 'react-redux-request';
import { loadErrorGroupDetails } from '../../services/rest/apm';
import { loadErrorGroupDetails } from '../../services/rest/apm/error_groups';
const ID = 'errorGroupDetails';
const INITIAL_DATA = {};

View file

@ -7,7 +7,7 @@
import React from 'react';
import { createInitialDataSelector } from './helpers';
import { Request } from 'react-redux-request';
import { loadErrorGroupList } from '../../services/rest/apm';
import { loadErrorGroupList } from '../../services/rest/apm/error_groups';
const ID = 'errorGroupList';
const INITIAL_DATA = [];

View file

@ -6,7 +6,7 @@
import React from 'react';
import { createInitialDataSelector } from './helpers';
import { Request } from 'react-redux-request';
import { loadLicense } from '../../services/rest/apm';
import { loadLicense } from '../../services/rest/xpack';
const ID = 'license';
const INITIAL_DATA = {

View file

@ -9,7 +9,7 @@ import _ from 'lodash';
import PropTypes from 'prop-types';
import { createInitialDataSelector } from './helpers';
import { Request } from 'react-redux-request';
import { loadServiceDetails } from '../../services/rest/apm';
import { loadServiceDetails } from '../../services/rest/apm/services';
const ID = 'serviceDetails';
const INITIAL_DATA = { types: [] };

View file

@ -7,7 +7,7 @@
import React from 'react';
import { Request, RRRRender, RRRRenderResponse } from 'react-redux-request';
import { IServiceListItem } from 'x-pack/plugins/apm/server/lib/services/get_services';
import { loadServiceList } from '../../services/rest/apm';
import { loadServiceList } from '../../services/rest/apm/services';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';
// @ts-ignore

View file

@ -7,7 +7,7 @@
import React from 'react';
import { Request } from 'react-redux-request';
import { createSelector } from 'reselect';
import { loadTraceList } from '../../services/rest/apm';
import { loadTraceList } from '../../services/rest/apm/traces';
import { createInitialDataSelector } from './helpers';
const ID = 'traceList';

View file

@ -7,7 +7,7 @@
import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { loadTransaction } from '../../services/rest/apm';
import { loadTransaction } from '../../services/rest/apm/transactions';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';
// @ts-ignore

View file

@ -8,7 +8,7 @@ import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { createSelector } from 'reselect';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { loadCharts } from '../../services/rest/apm';
import { loadDetailsCharts } from '../../services/rest/apm/transaction_groups';
import { IReduxState } from '../rootReducer';
import { getCharts } from '../selectors/chartSelectors';
import { getUrlParams, IUrlParams } from '../urlParams';
@ -61,7 +61,7 @@ export function TransactionDetailsChartsRequest({ urlParams, render }: Props) {
return (
<Request
id={ID}
fn={loadCharts}
fn={loadDetailsCharts}
args={[
{ serviceName, start, end, transactionType, transactionName, kuery }
]}

View file

@ -7,7 +7,7 @@
import React from 'react';
import { Request, RRRRender, RRRRenderResponse } from 'react-redux-request';
import { ITransactionDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/distribution';
import { loadTransactionDistribution } from '../../services/rest/apm';
import { loadTransactionDistribution } from '../../services/rest/apm/transaction_groups';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';
// @ts-ignore
@ -41,6 +41,7 @@ export function TransactionDistributionRequest({
}) {
const {
serviceName,
transactionType,
transactionId,
start,
end,
@ -48,7 +49,7 @@ export function TransactionDistributionRequest({
kuery
} = urlParams;
if (!(serviceName && start && end && transactionName)) {
if (!(serviceName && transactionType && start && end && transactionName)) {
return null;
}
@ -57,7 +58,15 @@ export function TransactionDistributionRequest({
id={ID}
fn={loadTransactionDistribution}
args={[
{ serviceName, transactionId, start, end, transactionName, kuery }
{
serviceName,
transactionType,
transactionId,
start,
end,
transactionName,
kuery
}
]}
selector={getTransactionDistribution}
render={render}

View file

@ -7,7 +7,7 @@
import React from 'react';
import { createSelector } from 'reselect';
import { Request } from 'react-redux-request';
import { loadTransactionList } from '../../services/rest/apm';
import { loadTransactionList } from '../../services/rest/apm/transaction_groups';
import { createInitialDataSelector } from './helpers';
const ID = 'transactionList';

View file

@ -9,7 +9,7 @@ import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { createSelector } from 'reselect';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { loadCharts } from '../../services/rest/apm';
import { loadOverviewCharts } from '../../services/rest/apm/transaction_groups';
import { IReduxState } from '../rootReducer';
import { getCharts } from '../selectors/chartSelectors';
import { getUrlParams, IUrlParams } from '../urlParams';
@ -61,7 +61,7 @@ export function TransactionOverviewChartsRequest({ urlParams, render }: Props) {
return (
<Request
id={ID}
fn={loadCharts}
fn={loadOverviewCharts}
args={[{ serviceName, start, end, transactionType, kuery }]}
selector={getTransactionOverviewCharts}
render={render}

View file

@ -17,7 +17,7 @@ import {
getWaterfall,
IWaterfall
} from '../../components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
import { loadSpans } from '../../services/rest/apm';
import { loadSpans } from '../../services/rest/apm/transactions';
import { IUrlParams } from '../urlParams';
// @ts-ignore
import { createInitialDataSelector } from './helpers';

View file

@ -14,7 +14,7 @@ import {
getWaterfall,
IWaterfall
} from '../../components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
import { loadTrace } from '../../services/rest/apm';
import { loadTrace } from '../../services/rest/apm/traces';
import { IUrlParams } from '../urlParams';
// @ts-ignore
import { createInitialDataSelector } from './helpers';

View file

@ -26,6 +26,9 @@ Array [
"top_hits": Object {
"size": 1,
"sort": Array [
Object {
"_score": "desc",
},
Object {
"@timestamp": Object {
"order": "desc",
@ -34,11 +37,16 @@ Array [
],
},
},
"sum": Object {
"sum": Object {
"field": "transaction.duration.us",
},
},
},
"terms": Object {
"field": "transaction.name.keyword",
"order": Object {
"avg": "desc",
"sum": "desc",
},
"size": 100,
},

View file

@ -4,7 +4,7 @@ exports[`transactionGroupsTransformer should match snapshot 1`] = `
Array [
Object {
"averageResponseTime": 255966.30555555556,
"impact": 4.369340653255684,
"impact": 4.3693406535517445,
"name": "POST /api/orders",
"p95": 320238.5,
"sample": Object {
@ -255,7 +255,7 @@ baz",
},
Object {
"averageResponseTime": 33265.03326147213,
"impact": 10.256357027900046,
"impact": 10.256357027376065,
"name": "GET /api/orders",
"p95": 58827.489999999976,
"sample": Object {
@ -376,7 +376,7 @@ baz",
},
Object {
"averageResponseTime": 32900.72714285714,
"impact": 2.179120743402716,
"impact": 2.1791207411745854,
"name": "GET /log-message",
"p95": 40444,
"sample": Object {
@ -497,7 +497,7 @@ baz",
},
Object {
"averageResponseTime": 32554.36257814184,
"impact": 14.344171564855404,
"impact": 14.344171563678346,
"name": "GET /api/stats",
"p95": 59356.73611111111,
"sample": Object {
@ -624,7 +624,7 @@ baz",
},
Object {
"averageResponseTime": 32387.73641304348,
"impact": 2.2558112391673664,
"impact": 2.2558112380477584,
"name": "GET /log-error",
"p95": 40061.1,
"sample": Object {
@ -745,7 +745,7 @@ baz",
},
Object {
"averageResponseTime": 32159.926322043968,
"impact": 10.279049521913821,
"impact": 10.27904952170656,
"name": "GET /api/customers",
"p95": 59845.85714285714,
"sample": Object {
@ -872,7 +872,7 @@ baz",
},
Object {
"averageResponseTime": 27516.89144558744,
"impact": 9.651458993728006,
"impact": 9.651458992731666,
"name": "GET /api/products/top",
"p95": 56064.679999999986,
"sample": Object {
@ -1000,7 +1000,7 @@ baz",
},
Object {
"averageResponseTime": 21331.714285714286,
"impact": 0.28817488008070574,
"impact": 0.28817487960409877,
"name": "POST /api",
"p95": 30938,
"sample": Object {
@ -1123,7 +1123,7 @@ baz",
},
Object {
"averageResponseTime": 17189.329210275926,
"impact": 3.424381788267164,
"impact": 3.424381787142002,
"name": "GET /api/products/:id/customers",
"p95": 39284.79999999999,
"sample": Object {
@ -1250,7 +1250,7 @@ baz",
},
Object {
"averageResponseTime": 12763.68806073154,
"impact": 1.747992435179465,
"impact": 1.7479924334286208,
"name": "GET /api/types/:id",
"p95": 30576.749999999996,
"sample": Object {
@ -1371,7 +1371,7 @@ baz",
},
Object {
"averageResponseTime": 12683.190864600327,
"impact": 4.4239778511514745,
"impact": 4.4239778504968,
"name": "GET /api/products",
"p95": 35009.67999999999,
"sample": Object {
@ -1492,7 +1492,7 @@ baz",
},
Object {
"averageResponseTime": 11257.757916666667,
"impact": 2.558180605423081,
"impact": 2.558180605569336,
"name": "GET /api/types",
"p95": 35222.944444444445,
"sample": Object {
@ -1613,7 +1613,7 @@ baz",
},
Object {
"averageResponseTime": 10584.05144193297,
"impact": 1.2808106158729446,
"impact": 1.280810614916383,
"name": "GET /api/orders/:id",
"p95": 26555.399999999998,
"sample": Object {
@ -1732,7 +1732,7 @@ baz",
},
Object {
"averageResponseTime": 10548.218597063622,
"impact": 1.8338764008269306,
"impact": 1.8338763992340905,
"name": "GET /api/products/:id",
"p95": 28413.383333333328,
"sample": Object {
@ -1853,7 +1853,7 @@ baz",
},
Object {
"averageResponseTime": 9868.217894736843,
"impact": 1.7722323979309487,
"impact": 1.7722323960215767,
"name": "GET /api/customers/:id",
"p95": 27486.5,
"sample": Object {
@ -2111,7 +2111,7 @@ baz",
},
Object {
"averageResponseTime": 4694.005586592179,
"impact": 0.1498514997591876,
"impact": 0.1498515000753004,
"name": "GET /is-it-coffee-time",
"p95": 11022.99999999992,
"sample": Object {
@ -2233,7 +2233,7 @@ baz",
},
Object {
"averageResponseTime": 4549.889880952381,
"impact": 0.1354336505457395,
"impact": 0.13543365054509587,
"name": "GET /throw-error",
"p95": 7719.700000000001,
"sample": Object {
@ -2355,7 +2355,7 @@ baz",
},
Object {
"averageResponseTime": 3504.5108924806746,
"impact": 2.36009934580083,
"impact": 2.3600993453143766,
"name": "GET *",
"p95": 11431.738095238095,
"sample": Object {
@ -2482,7 +2482,7 @@ baz",
},
Object {
"averageResponseTime": 2742.4615384615386,
"impact": 0.08501029113483448,
"impact": 0.08501028923348058,
"name": "OPTIONS unknown route",
"p95": 4370.000000000002,
"sample": Object {
@ -2605,7 +2605,7 @@ baz",
},
Object {
"averageResponseTime": 2651.8784461553205,
"impact": 15.770246498769827,
"impact": 15.770246496477105,
"name": "GET static file",
"p95": 6140.579335038363,
"sample": Object {
@ -2713,7 +2713,7 @@ baz",
},
Object {
"averageResponseTime": 1422.926672899693,
"impact": 1.002712481568783,
"impact": 1.0027124806135428,
"name": "GET unknown route",
"p95": 2311.885238095238,
"sample": Object {

View file

@ -16,14 +16,9 @@ import { Setup } from '../helpers/setup_request';
interface Bucket {
key: string;
doc_count: number;
avg: {
value: number;
};
p95: {
values: {
'95.0': number;
};
};
avg: { value: number };
p95: { values: { '95.0': number } };
sum: { value: number };
sample: {
hits: {
total: number;
@ -57,20 +52,24 @@ export function transactionGroupsFetcher(
transactions: {
terms: {
field: `${TRANSACTION_NAME}.keyword`,
order: { avg: 'desc' },
order: { sum: 'desc' },
size: 100
},
aggs: {
sample: {
top_hits: {
size: 1,
sort: [{ '@timestamp': { order: 'desc' } }]
sort: [
{ _score: 'desc' }, // sort by _score to ensure that buckets with sampled:true ends up on top
{ '@timestamp': { order: 'desc' } }
]
}
},
avg: { avg: { field: TRANSACTION_DURATION } },
p95: {
percentiles: { field: TRANSACTION_DURATION, percents: [95] }
}
},
sum: { sum: { field: TRANSACTION_DURATION } }
}
}
}

View file

@ -21,6 +21,7 @@ export const transactionGroupsResponse = ({
doc_count: 180,
avg: { value: 255966.30555555556 },
p95: { values: { '95.0': 320238.5 } },
sum: { value: 46073935 },
sample: {
hits: {
total: 180,
@ -140,6 +141,7 @@ export const transactionGroupsResponse = ({
doc_count: 21911,
avg: { value: 48021.972616494 },
p95: { values: { '95.0': 67138.18364917398 } },
sum: { value: 1052209442 },
sample: {
hits: {
total: 21911,
@ -259,6 +261,7 @@ export const transactionGroupsResponse = ({
doc_count: 3247,
avg: { value: 33265.03326147213 },
p95: { values: { '95.0': 58827.489999999976 } },
sum: { value: 108011563 },
sample: {
hits: {
total: 3247,
@ -374,6 +377,7 @@ export const transactionGroupsResponse = ({
doc_count: 700,
avg: { value: 32900.72714285714 },
p95: { values: { '95.0': 40444 } },
sum: { value: 23030509 },
sample: {
hits: {
total: 700,
@ -489,6 +493,7 @@ export const transactionGroupsResponse = ({
doc_count: 4639,
avg: { value: 32554.36257814184 },
p95: { values: { '95.0': 59356.73611111111 } },
sum: { value: 151019688 },
sample: {
hits: {
total: 4639,
@ -609,6 +614,7 @@ export const transactionGroupsResponse = ({
doc_count: 736,
avg: { value: 32387.73641304348 },
p95: { values: { '95.0': 40061.1 } },
sum: { value: 23837374 },
sample: {
hits: {
total: 736,
@ -724,6 +730,7 @@ export const transactionGroupsResponse = ({
doc_count: 3366,
avg: { value: 32159.926322043968 },
p95: { values: { '95.0': 59845.85714285714 } },
sum: { value: 108250312 },
sample: {
hits: {
total: 3366,
@ -844,6 +851,7 @@ export const transactionGroupsResponse = ({
doc_count: 3694,
avg: { value: 27516.89144558744 },
p95: { values: { '95.0': 56064.679999999986 } },
sum: { value: 101647397 },
sample: {
hits: {
total: 3694,
@ -965,6 +973,7 @@ export const transactionGroupsResponse = ({
doc_count: 147,
avg: { value: 21331.714285714286 },
p95: { values: { '95.0': 30938 } },
sum: { value: 3135762 },
sample: {
hits: {
total: 147,
@ -1082,6 +1091,7 @@ export const transactionGroupsResponse = ({
doc_count: 2102,
avg: { value: 17189.329210275926 },
p95: { values: { '95.0': 39284.79999999999 } },
sum: { value: 36131970 },
sample: {
hits: {
total: 2102,
@ -1203,6 +1213,7 @@ export const transactionGroupsResponse = ({
doc_count: 1449,
avg: { value: 12763.68806073154 },
p95: { values: { '95.0': 30576.749999999996 } },
sum: { value: 18494584 },
sample: {
hits: {
total: 1449,
@ -1318,6 +1329,7 @@ export const transactionGroupsResponse = ({
doc_count: 3678,
avg: { value: 12683.190864600327 },
p95: { values: { '95.0': 35009.67999999999 } },
sum: { value: 46648776 },
sample: {
hits: {
total: 3678,
@ -1433,6 +1445,7 @@ export const transactionGroupsResponse = ({
doc_count: 2400,
avg: { value: 11257.757916666667 },
p95: { values: { '95.0': 35222.944444444445 } },
sum: { value: 27018619 },
sample: {
hits: {
total: 2400,
@ -1548,6 +1561,7 @@ export const transactionGroupsResponse = ({
doc_count: 1283,
avg: { value: 10584.05144193297 },
p95: { values: { '95.0': 26555.399999999998 } },
sum: { value: 13579338 },
sample: {
hits: {
total: 1283,
@ -1661,6 +1675,7 @@ export const transactionGroupsResponse = ({
doc_count: 1839,
avg: { value: 10548.218597063622 },
p95: { values: { '95.0': 28413.383333333328 } },
sum: { value: 19398174 },
sample: {
hits: {
total: 1839,
@ -1776,6 +1791,7 @@ export const transactionGroupsResponse = ({
doc_count: 1900,
avg: { value: 9868.217894736843 },
p95: { values: { '95.0': 27486.5 } },
sum: { value: 18749614 },
sample: {
hits: {
total: 1900,
@ -1896,6 +1912,7 @@ export const transactionGroupsResponse = ({
doc_count: 20,
avg: { value: 5192.9 },
p95: { values: { '95.0': 13230.5 } },
sum: { value: 103858 },
sample: {
hits: {
total: 20,
@ -2021,6 +2038,7 @@ export const transactionGroupsResponse = ({
doc_count: 358,
avg: { value: 4694.005586592179 },
p95: { values: { '95.0': 11022.99999999992 } },
sum: { value: 1680454 },
sample: {
hits: {
total: 358,
@ -2137,6 +2155,7 @@ export const transactionGroupsResponse = ({
doc_count: 336,
avg: { value: 4549.889880952381 },
p95: { values: { '95.0': 7719.700000000001 } },
sum: { value: 1528763 },
sample: {
hits: {
total: 336,
@ -2253,6 +2272,7 @@ export const transactionGroupsResponse = ({
doc_count: 7115,
avg: { value: 3504.5108924806746 },
p95: { values: { '95.0': 11431.738095238095 } },
sum: { value: 24934595 },
sample: {
hits: {
total: 7115,
@ -2375,6 +2395,7 @@ export const transactionGroupsResponse = ({
doc_count: 364,
avg: { value: 2742.4615384615386 },
p95: { values: { '95.0': 4370.000000000002 } },
sum: { value: 998256 },
sample: {
hits: {
total: 364,
@ -2492,6 +2513,7 @@ export const transactionGroupsResponse = ({
doc_count: 62606,
avg: { value: 2651.8784461553205 },
p95: { values: { '95.0': 6140.579335038363 } },
sum: { value: 166023502 },
sample: {
hits: {
total: 62606,
@ -2596,6 +2618,7 @@ export const transactionGroupsResponse = ({
doc_count: 7487,
avg: { value: 1422.926672899693 },
p95: { values: { '95.0': 2311.885238095238 } },
sum: { value: 10653452 },
sample: {
hits: {
total: 7487,

View file

@ -19,12 +19,13 @@ describe('transactionGroupsTransformer', () => {
).toMatchSnapshot();
});
fit('should transform response correctly', () => {
it('should transform response correctly', () => {
const bucket = {
key: 'POST /api/orders',
doc_count: 180,
avg: { value: 255966.30555555556 },
p95: { values: { '95.0': 320238.5 } },
sum: { value: 3000000000 },
sample: {
hits: {
total: 180,
@ -54,4 +55,27 @@ describe('transactionGroupsTransformer', () => {
}
]);
});
it('should calculate impact from sum', () => {
const getBucket = (sum: number) => ({
key: 'POST /api/orders',
doc_count: 180,
avg: { value: 300000 },
p95: { values: { '95.0': 320000 } },
sum: { value: sum },
sample: { hits: { total: 180, hits: [{ _source: 'sample source' }] } }
});
const response = ({
aggregations: {
transactions: { buckets: [getBucket(10), getBucket(20), getBucket(50)] }
}
} as unknown) as ESResponse;
expect(
transactionGroupsTransformer({ response, start: 100, end: 20000 }).map(
bucket => bucket.impact
)
).toEqual([0, 25, 100]);
});
});

View file

@ -44,7 +44,7 @@ export function transactionGroupsTransformer({
const results = buckets.map(bucket => {
const averageResponseTime = bucket.avg.value;
const transactionsPerMinute = bucket.doc_count / minutes;
const impact = Math.round(averageResponseTime * transactionsPerMinute);
const impact = bucket.sum.value;
const sample = bucket.sample.hits.hits[0]._source;
return {

View file

@ -8,13 +8,15 @@ import { SearchParams } from 'elasticsearch';
import {
SERVICE_NAME,
TRANSACTION_DURATION,
TRANSACTION_NAME
TRANSACTION_NAME,
TRANSACTION_TYPE
} from '../../../../common/constants';
import { Setup } from '../../helpers/setup_request';
export async function calculateBucketSize(
serviceName: string,
transactionName: string,
transactionType: string,
setup: Setup
) {
const { start, end, esFilterQuery, client, config } = setup;
@ -27,6 +29,7 @@ export async function calculateBucketSize(
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [`${TRANSACTION_NAME}.keyword`]: transactionName } },
{
range: {

View file

@ -11,7 +11,8 @@ import {
TRANSACTION_DURATION,
TRANSACTION_ID,
TRANSACTION_NAME,
TRANSACTION_SAMPLED
TRANSACTION_SAMPLED,
TRANSACTION_TYPE
} from 'x-pack/plugins/apm/common/constants';
import { Setup } from 'x-pack/plugins/apm/server/lib/helpers/setup_request';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
@ -38,6 +39,7 @@ export type ESResponse = AggregationSearchResponse<void, Aggs>;
export function bucketFetcher(
serviceName: string,
transactionName: string,
transactionType: string,
transactionId: string,
bucketSize: number,
setup: Setup
@ -52,6 +54,7 @@ export function bucketFetcher(
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [`${TRANSACTION_NAME}.keyword`]: transactionName } },
{
range: {

View file

@ -11,6 +11,7 @@ import { bucketTransformer } from './transform';
export async function getBuckets(
serviceName: string,
transactionName: string,
transactionType: string,
transactionId: string,
bucketSize: number,
setup: Setup
@ -18,6 +19,7 @@ export async function getBuckets(
const response = await bucketFetcher(
serviceName,
transactionName,
transactionType,
transactionId,
bucketSize,
setup

View file

@ -19,18 +19,21 @@ export interface ITransactionDistributionAPIResponse {
export async function getDistribution(
serviceName: string,
transactionName: string,
transactionType: string,
transactionId: string,
setup: Setup
): Promise<ITransactionDistributionAPIResponse> {
const bucketSize = await calculateBucketSize(
serviceName,
transactionName,
transactionType,
setup
);
const { defaultSample, buckets, totalHits } = await getBuckets(
serviceName,
transactionName,
transactionType,
transactionId,
bucketSize,
setup

View file

@ -0,0 +1,110 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import Boom from 'boom';
import { Server } from 'hapi';
import Joi from 'joi';
import { withDefaultValidators } from '../lib/helpers/input_validation';
import { setupRequest } from '../lib/helpers/setup_request';
import { getChartsData } from '../lib/transactions/charts';
import { getDistribution } from '../lib/transactions/distribution';
import { getTopTransactions } from '../lib/transactions/get_top_transactions';
const defaultErrorHandler = (err: Error) => {
// tslint:disable-next-line
console.error(err.stack);
throw Boom.boomify(err, { statusCode: 400 });
};
export function initTransactionGroupsApi(server: Server) {
server.route({
method: 'GET',
path:
'/api/apm/services/{serviceName}/transaction_groups/{transactionType}',
options: {
validate: {
query: withDefaultValidators({
query: Joi.string()
})
}
},
handler: req => {
const { serviceName, transactionType } = req.params;
const setup = setupRequest(req);
return getTopTransactions({
serviceName,
transactionType,
setup
}).catch(defaultErrorHandler);
}
});
server.route({
method: 'GET',
path: `/api/apm/services/{serviceName}/transaction_groups/{transactionType}/charts`,
options: {
validate: {
query: withDefaultValidators()
}
},
handler: req => {
const setup = setupRequest(req);
const { serviceName, transactionType } = req.params;
return getChartsData({
serviceName,
transactionType,
setup
}).catch(defaultErrorHandler);
}
});
server.route({
method: 'GET',
path: `/api/apm/services/{serviceName}/transaction_groups/{transactionType}/{transactionName}/charts`,
options: {
validate: {
query: withDefaultValidators()
}
},
handler: req => {
const setup = setupRequest(req);
const { serviceName, transactionType, transactionName } = req.params;
return getChartsData({
serviceName,
transactionType,
transactionName,
setup
}).catch(defaultErrorHandler);
}
});
server.route({
method: 'GET',
path: `/api/apm/services/{serviceName}/transaction_groups/{transactionType}/{transactionName}/distribution`,
options: {
validate: {
query: withDefaultValidators({
transactionId: Joi.string().default('')
})
}
},
handler: req => {
const setup = setupRequest(req);
const { serviceName, transactionType, transactionName } = req.params;
const { transactionId } = req.query as { transactionId: string };
return getDistribution(
serviceName,
transactionName,
transactionType,
transactionId,
setup
).catch(defaultErrorHandler);
}
});
}

View file

@ -9,13 +9,9 @@ import { Server } from 'hapi';
import Joi from 'joi';
import { withDefaultValidators } from '../lib/helpers/input_validation';
import { setupRequest } from '../lib/helpers/setup_request';
import { getChartsData } from '../lib/transactions/charts';
import { getDistribution } from '../lib/transactions/distribution';
import { getTopTransactions } from '../lib/transactions/get_top_transactions';
import { getTransaction } from '../lib/transactions/get_transaction';
import { getSpans } from '../lib/transactions/spans/get_spans';
const ROOT = '/api/apm/services/{serviceName}/transactions';
const defaultErrorHandler = (err: Error) => {
// tslint:disable-next-line
console.error(err.stack);
@ -25,37 +21,11 @@ const defaultErrorHandler = (err: Error) => {
export function initTransactionsApi(server: Server) {
server.route({
method: 'GET',
path: ROOT,
path: `/api/apm/services/{serviceName}/transactions/{transactionId}`,
options: {
validate: {
query: withDefaultValidators({
transaction_type: Joi.string().default('request'),
query: Joi.string()
})
}
},
handler: req => {
const { serviceName } = req.params;
const { transaction_type: transactionType } = req.query as {
transaction_type: string;
};
const setup = setupRequest(req);
return getTopTransactions({
serviceName,
transactionType,
setup
}).catch(defaultErrorHandler);
}
});
server.route({
method: 'GET',
path: `${ROOT}/{transactionId}`,
options: {
validate: {
query: withDefaultValidators({
traceId: Joi.string().allow('')
traceId: Joi.string().allow('') // TODO: this should be a path param and made required by 7.0
})
}
},
@ -69,9 +39,10 @@ export function initTransactionsApi(server: Server) {
}
});
// TODO: this can be removed by 7.0 when v1 compatability can be dropped
server.route({
method: 'GET',
path: `${ROOT}/{transactionId}/spans`,
path: `/api/apm/services/{serviceName}/transactions/{transactionId}/spans`,
options: {
validate: {
query: withDefaultValidators()
@ -83,65 +54,4 @@ export function initTransactionsApi(server: Server) {
return getSpans(transactionId, setup).catch(defaultErrorHandler);
}
});
server.route({
method: 'GET',
path: `${ROOT}/charts`,
options: {
validate: {
query: withDefaultValidators({
transaction_type: Joi.string().default('request'),
transaction_name: Joi.string(),
query: Joi.string()
})
}
},
handler: req => {
const setup = setupRequest(req);
const { serviceName } = req.params;
const { transaction_type: transactionType } = req.query as {
transaction_type: string;
};
const { transaction_name: transactionName } = req.query as {
transaction_name: string;
};
return getChartsData({
serviceName,
transactionType,
transactionName,
setup
}).catch(defaultErrorHandler);
}
});
server.route({
method: 'GET',
path: `${ROOT}/distribution`,
options: {
validate: {
query: withDefaultValidators({
transaction_name: Joi.string().required(),
transaction_id: Joi.string().default('')
})
}
},
handler: req => {
const setup = setupRequest(req);
const { serviceName } = req.params;
const {
transaction_name: transactionName,
transaction_id: transactionId
} = req.query as {
transaction_name: string;
transaction_id: string;
};
return getDistribution(
serviceName,
transactionName,
transactionId,
setup
).catch(defaultErrorHandler);
}
});
}