kibana/x-pack/plugins/apm/server/routes/ui_filters.ts
Søren Louv-Jansen 4ddcd1d2a6
[APM] Fix anomalies not showing up on transaction charts (#76930)
* [APM] Fix anomalies not showing up on transaction charts

* Added API tests to check transaction groups charts for anomaly data

* Improve test names and assertions from PR feedback

* Updated the transaction groups chart API to make `environment` a
required param while making `uiFilters` optional

* updates the basic API tests for transaction_groups/charts with the
required `environment` param

* makes uiFIltersES default to [] on core setup and removes SetupUIFilters type

* fixes vertical shade

* - replaces uiFiltersES with esFilter & uiFilters and cleans up related code around these
- deduplicates the required environment in the transaction_groups/charts API

* updates basic apm_api_integration tests

* pr feedback

* updates api test snapshots with correct anomaly data

* removed environment query param from useTransactionCharts and ensures
it's included in uiFilters returned from useUrlParams

Co-authored-by: Oliver Gupte <olivergupte@gmail.com>
2020-10-02 00:45:50 -07:00

276 lines
7.4 KiB
TypeScript

/*
* 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 * as t from 'io-ts';
import { omit } from 'lodash';
import {
setupRequest,
Setup,
SetupTimeRange,
} from '../lib/helpers/setup_request';
import { getEnvironments } from '../lib/ui_filters/get_environments';
import { Projection } from '../projections/typings';
import { localUIFilterNames } from '../lib/ui_filters/local_ui_filters/config';
import { getEsFilter } from '../lib/helpers/convert_ui_filters/get_es_filter';
import { getLocalUIFilters } from '../lib/ui_filters/local_ui_filters';
import { getServicesProjection } from '../projections/services';
import { getTransactionGroupsProjection } from '../projections/transaction_groups';
import { getMetricsProjection } from '../projections/metrics';
import { getErrorGroupsProjection } from '../projections/errors';
import { getTransactionsProjection } from '../projections/transactions';
import { createRoute } from './create_route';
import { uiFiltersRt, rangeRt } from './default_api_types';
import { jsonRt } from '../../common/runtime_types/json_rt';
import { getServiceNodesProjection } from '../projections/service_nodes';
import { getRumPageLoadTransactionsProjection } from '../projections/rum_page_load_transactions';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { APMRequestHandlerContext } from './typings';
import { LocalUIFilterName } from '../../common/ui_filter';
export const uiFiltersEnvironmentsRoute = createRoute(() => ({
path: '/api/apm/ui_filters/environments',
params: {
query: t.intersection([
t.partial({
serviceName: t.string,
}),
rangeRt,
]),
},
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { serviceName } = context.params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getEnvironments({
setup,
serviceName,
searchAggregatedTransactions,
});
},
}));
const filterNamesRt = t.type({
filterNames: jsonRt.pipe(
t.array(
t.keyof(
Object.fromEntries(
localUIFilterNames.map((filterName) => [filterName, null])
) as Record<LocalUIFilterName, null>
)
)
),
});
const localUiBaseQueryRt = t.intersection([
filterNamesRt,
uiFiltersRt,
rangeRt,
]);
function createLocalFiltersRoute<
TPath extends string,
TProjection extends Projection,
TQueryRT extends t.HasProps
>({
path,
getProjection,
queryRt,
}: {
path: TPath;
getProjection: GetProjection<
TProjection,
t.IntersectionC<[TQueryRT, BaseQueryType]>
>;
queryRt: TQueryRT;
}) {
return createRoute(() => ({
path,
params: {
query: t.intersection([localUiBaseQueryRt, queryRt]),
},
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { uiFilters } = setup;
const { query } = context.params;
const { filterNames } = query;
const projection = await getProjection({
query,
context,
setup: {
...setup,
esFilter: getEsFilter(omit(uiFilters, filterNames)),
},
});
return getLocalUIFilters({
projection,
setup,
uiFilters,
localFilterNames: filterNames,
});
},
}));
}
export const servicesLocalFiltersRoute = createLocalFiltersRoute({
path: `/api/apm/ui_filters/local_filters/services`,
getProjection: async ({ context, setup }) => {
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getServicesProjection({ setup, searchAggregatedTransactions });
},
queryRt: t.type({}),
});
export const transactionGroupsLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/transactionGroups',
getProjection: async ({ context, setup, query }) => {
const { transactionType, serviceName, transactionName } = query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getTransactionGroupsProjection({
setup,
options: {
type: 'top_transactions',
transactionType,
serviceName,
transactionName,
searchAggregatedTransactions,
},
});
},
queryRt: t.intersection([
t.type({
serviceName: t.string,
transactionType: t.string,
}),
t.partial({
transactionName: t.string,
}),
]),
});
export const tracesLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/traces',
getProjection: async ({ setup, context }) => {
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getTransactionGroupsProjection({
setup,
options: { type: 'top_traces', searchAggregatedTransactions },
});
},
queryRt: t.type({}),
});
export const transactionsLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/transactions',
getProjection: async ({ context, setup, query }) => {
const { transactionType, serviceName, transactionName } = query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getTransactionsProjection({
setup,
transactionType,
serviceName,
transactionName,
searchAggregatedTransactions,
});
},
queryRt: t.type({
transactionType: t.string,
transactionName: t.string,
serviceName: t.string,
}),
});
export const metricsLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/metrics',
getProjection: ({ setup, query }) => {
const { serviceName, serviceNodeName } = query;
return getMetricsProjection({
setup,
serviceName,
serviceNodeName,
});
},
queryRt: t.intersection([
t.type({
serviceName: t.string,
}),
t.partial({
serviceNodeName: t.string,
}),
]),
});
export const errorGroupsLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/errorGroups',
getProjection: ({ setup, query }) => {
const { serviceName } = query;
return getErrorGroupsProjection({
setup,
serviceName,
});
},
queryRt: t.type({
serviceName: t.string,
}),
});
export const serviceNodesLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/serviceNodes',
getProjection: ({ setup, query }) => {
const { serviceName } = query;
return getServiceNodesProjection({
setup,
serviceName,
});
},
queryRt: t.type({
serviceName: t.string,
}),
});
export const rumOverviewLocalFiltersRoute = createLocalFiltersRoute({
path: '/api/apm/ui_filters/local_filters/rumOverview',
getProjection: async ({ setup }) => {
return getRumPageLoadTransactionsProjection({
setup,
});
},
queryRt: t.type({}),
});
type BaseQueryType = typeof localUiBaseQueryRt;
type GetProjection<
TProjection extends Projection,
TQueryRT extends t.HasProps
> = ({
query,
setup,
context,
}: {
query: t.TypeOf<TQueryRT>;
setup: Setup & SetupTimeRange;
context: APMRequestHandlerContext;
}) => Promise<TProjection> | TProjection;