* [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>
194 lines
5.3 KiB
TypeScript
194 lines
5.3 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 Boom from 'boom';
|
|
import { uniq } from 'lodash';
|
|
import { setupRequest } from '../lib/helpers/setup_request';
|
|
import { getServiceAgentName } from '../lib/services/get_service_agent_name';
|
|
import { getServices } from '../lib/services/get_services';
|
|
import { getServiceTransactionTypes } from '../lib/services/get_service_transaction_types';
|
|
import { getServiceNodeMetadata } from '../lib/services/get_service_node_metadata';
|
|
import { createRoute } from './create_route';
|
|
import { uiFiltersRt, rangeRt } from './default_api_types';
|
|
import { getServiceAnnotations } from '../lib/services/annotations';
|
|
import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt';
|
|
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
|
|
|
|
export const servicesRoute = createRoute(() => ({
|
|
path: '/api/apm/services',
|
|
params: {
|
|
query: t.intersection([uiFiltersRt, rangeRt]),
|
|
},
|
|
handler: async ({ context, request }) => {
|
|
const setup = await setupRequest(context, request);
|
|
|
|
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
|
|
setup
|
|
);
|
|
|
|
const services = await getServices({ setup, searchAggregatedTransactions });
|
|
|
|
return services;
|
|
},
|
|
}));
|
|
|
|
export const serviceAgentNameRoute = createRoute(() => ({
|
|
path: '/api/apm/services/{serviceName}/agent_name',
|
|
params: {
|
|
path: t.type({
|
|
serviceName: t.string,
|
|
}),
|
|
query: rangeRt,
|
|
},
|
|
handler: async ({ context, request }) => {
|
|
const setup = await setupRequest(context, request);
|
|
const { serviceName } = context.params.path;
|
|
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
|
|
setup
|
|
);
|
|
|
|
return getServiceAgentName({
|
|
serviceName,
|
|
setup,
|
|
searchAggregatedTransactions,
|
|
});
|
|
},
|
|
}));
|
|
|
|
export const serviceTransactionTypesRoute = createRoute(() => ({
|
|
path: '/api/apm/services/{serviceName}/transaction_types',
|
|
params: {
|
|
path: t.type({
|
|
serviceName: t.string,
|
|
}),
|
|
query: rangeRt,
|
|
},
|
|
handler: async ({ context, request }) => {
|
|
const setup = await setupRequest(context, request);
|
|
const { serviceName } = context.params.path;
|
|
return getServiceTransactionTypes({
|
|
serviceName,
|
|
setup,
|
|
searchAggregatedTransactions: await getSearchAggregatedTransactions(
|
|
setup
|
|
),
|
|
});
|
|
},
|
|
}));
|
|
|
|
export const serviceNodeMetadataRoute = createRoute(() => ({
|
|
path: '/api/apm/services/{serviceName}/node/{serviceNodeName}/metadata',
|
|
params: {
|
|
path: t.type({
|
|
serviceName: t.string,
|
|
serviceNodeName: t.string,
|
|
}),
|
|
query: t.intersection([uiFiltersRt, rangeRt]),
|
|
},
|
|
handler: async ({ context, request }) => {
|
|
const setup = await setupRequest(context, request);
|
|
const { serviceName, serviceNodeName } = context.params.path;
|
|
return getServiceNodeMetadata({ setup, serviceName, serviceNodeName });
|
|
},
|
|
}));
|
|
|
|
export const serviceAnnotationsRoute = createRoute(() => ({
|
|
path: '/api/apm/services/{serviceName}/annotation/search',
|
|
params: {
|
|
path: t.type({
|
|
serviceName: t.string,
|
|
}),
|
|
query: t.intersection([
|
|
rangeRt,
|
|
t.partial({
|
|
environment: t.string,
|
|
}),
|
|
]),
|
|
},
|
|
handler: async ({ context, request }) => {
|
|
const setup = await setupRequest(context, request);
|
|
const { serviceName } = context.params.path;
|
|
const { environment } = context.params.query;
|
|
|
|
const [
|
|
annotationsClient,
|
|
searchAggregatedTransactions,
|
|
] = await Promise.all([
|
|
context.plugins.observability?.getScopedAnnotationsClient(
|
|
context,
|
|
request
|
|
),
|
|
getSearchAggregatedTransactions(setup),
|
|
]);
|
|
|
|
return getServiceAnnotations({
|
|
setup,
|
|
searchAggregatedTransactions,
|
|
serviceName,
|
|
environment,
|
|
annotationsClient,
|
|
apiCaller: context.core.elasticsearch.legacy.client.callAsCurrentUser,
|
|
logger: context.logger,
|
|
});
|
|
},
|
|
}));
|
|
|
|
export const serviceAnnotationsCreateRoute = createRoute(() => ({
|
|
path: '/api/apm/services/{serviceName}/annotation',
|
|
method: 'POST',
|
|
options: {
|
|
tags: ['access:apm', 'access:apm_write'],
|
|
},
|
|
params: {
|
|
path: t.type({
|
|
serviceName: t.string,
|
|
}),
|
|
body: t.intersection([
|
|
t.type({
|
|
'@timestamp': dateAsStringRt,
|
|
service: t.intersection([
|
|
t.type({
|
|
version: t.string,
|
|
}),
|
|
t.partial({
|
|
environment: t.string,
|
|
}),
|
|
]),
|
|
}),
|
|
t.partial({
|
|
message: t.string,
|
|
tags: t.array(t.string),
|
|
}),
|
|
]),
|
|
},
|
|
handler: async ({ request, context }) => {
|
|
const annotationsClient = await context.plugins.observability?.getScopedAnnotationsClient(
|
|
context,
|
|
request
|
|
);
|
|
|
|
if (!annotationsClient) {
|
|
throw Boom.notFound();
|
|
}
|
|
|
|
const { body, path } = context.params;
|
|
|
|
return annotationsClient.create({
|
|
message: body.service.version,
|
|
...body,
|
|
annotation: {
|
|
type: 'deployment',
|
|
},
|
|
service: {
|
|
...body.service,
|
|
name: path.serviceName,
|
|
},
|
|
tags: uniq(['apm'].concat(body.tags ?? [])),
|
|
});
|
|
},
|
|
}));
|