[UX Dashboard] Remove search url query from APM server (#132654)

* Migrate service list query out of APM.

* Rename non-snakecase files.

* Migrate UX search filter input query out of APM server.

* Fix bug that could cause incorrect loading state to show.

* Remove obsolete API tests.

* Delete obsolete API tests.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Justin Kambic 2022-05-31 11:20:30 -04:00 committed by GitHub
parent 591aa648d6
commit a15e5ddea9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 71 additions and 192 deletions

View file

@ -14,7 +14,6 @@ import { getLongTaskMetrics } from './get_long_task_metrics';
import { getPageLoadDistribution } from './get_page_load_distribution';
import { getPageViewTrends } from './get_page_view_trends';
import { getPageLoadDistBreakdown } from './get_pl_dist_breakdown';
import { getUrlSearch } from './get_url_search';
import { getVisitorBreakdown } from './get_visitor_breakdown';
import { getWebCoreVitals } from './get_web_core_vitals';
import { hasRumData } from './has_rum_data';
@ -280,34 +279,6 @@ const rumLongTaskMetrics = createApmServerRoute({
},
});
const rumUrlSearch = createApmServerRoute({
endpoint: 'GET /internal/apm/ux/url-search',
params: t.type({
query: uxQueryRt,
}),
options: { tags: ['access:apm'] },
handler: async (
resources
): Promise<{
total: number;
items: Array<{ url: string; count: number; pld: number }>;
}> => {
const setup = await setupUXRequest(resources);
const {
query: { urlQuery, percentile, start, end },
} = resources.params;
return getUrlSearch({
setup,
urlQuery,
percentile: Number(percentile),
start,
end,
});
},
});
const rumJSErrors = createApmServerRoute({
endpoint: 'GET /internal/apm/ux/js-errors',
params: t.type({
@ -412,7 +383,6 @@ export const rumRouteRepository = {
...rumVisitorsBreakdownRoute,
...rumWebCoreVitals,
...rumLongTaskMetrics,
...rumUrlSearch,
...rumJSErrors,
...rumHasDataRoute,
};

View file

@ -6,20 +6,12 @@
*/
import React from 'react';
import datemath from '@kbn/datemath';
import { useEsSearch } from '@kbn/observability-plugin/public';
import { serviceNameQuery } from '../../../../services/data/service_name_query';
import { ServiceNameFilter } from '../url_filter/service_name_filter';
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
import { useDataView } from '../local_uifilters/use_data_view';
function callDateMath(value: unknown): number {
const DEFAULT_RETURN_VALUE = 0;
if (typeof value === 'string') {
return datemath.parse(value)?.valueOf() ?? DEFAULT_RETURN_VALUE;
}
return DEFAULT_RETURN_VALUE;
}
import { callDateMath } from '../../../../services/data/call_date_math';
export function WebApplicationSelect() {
const {
@ -42,6 +34,9 @@ export function WebApplicationSelect() {
data?.aggregations?.services?.buckets.map(({ key }) => key as string) ?? [];
return (
<ServiceNameFilter loading={!!loading} serviceNames={rumServiceNames} />
<ServiceNameFilter
loading={loading ?? true}
serviceNames={rumServiceNames}
/>
);
}

View file

@ -103,7 +103,7 @@ export function URLSearch({
const [items, setItems] = useState<UrlOption[]>([]);
const { data, status } = useUrlSearch({ query: searchValue, popoverIsOpen });
const { data, loading } = useUrlSearch({ query: searchValue, popoverIsOpen });
useEffect(() => {
const newItems = formatOptions(
@ -151,8 +151,6 @@ export function URLSearch({
setSearchValue(val);
};
const isLoading = status !== 'success';
const onApply = () => {
const { includedItems, excludedItems } = processItems(items);
@ -184,7 +182,7 @@ export function URLSearch({
return (
<SelectableUrlList
loading={isLoading}
loading={loading ?? true}
onInputChange={onInputChange}
data={{ items, total: data?.total ?? 0 }}
onSelectionChange={onChange}

View file

@ -6,10 +6,12 @@
*/
import useDebounce from 'react-use/lib/useDebounce';
import { useState } from 'react';
import { useFetcher } from '../../../../../hooks/use_fetcher';
import { useMemo, useState } from 'react';
import { useEsSearch } from '@kbn/observability-plugin/public';
import { useUxQuery } from '../../hooks/use_ux_query';
import { useLegacyUrlParams } from '../../../../../context/url_params_context/use_url_params';
import { useDataView } from '../../local_uifilters/use_data_view';
import { urlSearchQuery } from '../../../../../services/data/url_search_query';
interface Props {
popoverIsOpen: boolean;
@ -34,22 +36,33 @@ export const useUrlSearch = ({ popoverIsOpen, query }: Props) => {
[query]
);
return useFetcher(
(callApmApi) => {
if (uxQuery && popoverIsOpen) {
return callApmApi('GET /internal/apm/ux/url-search', {
params: {
query: {
...uxQuery,
uiFilters: JSON.stringify(restFilters),
urlQuery: searchValue,
},
},
});
}
return Promise.resolve(null);
const { dataViewTitle } = useDataView();
const { data: asyncSearchResult, loading } = useEsSearch(
{
// when `index` is undefined, the hook will not send a request,
// so we pass this to ensure the search values load lazily
index: uxQuery && popoverIsOpen ? dataViewTitle : undefined,
...urlSearchQuery(restFilters, uxQuery, searchValue),
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[uxQuery, searchValue, popoverIsOpen]
[dataViewTitle, popoverIsOpen, uxQuery, searchValue],
{ name: 'UX_URL_SEARCH' }
);
const data = useMemo(() => {
if (!asyncSearchResult) return asyncSearchResult;
const { urls, totalUrls } = asyncSearchResult.aggregations ?? {};
const pkey = Number(uxQuery?.percentile ?? 0).toFixed(1);
return {
total: totalUrls?.value || 0,
items: (urls?.buckets ?? []).map((bucket) => ({
url: bucket.key as string,
count: bucket.doc_count,
pld: bucket.medianPLD.values[pkey] ?? 0,
})),
};
}, [asyncSearchResult, uxQuery?.percentile]);
return { data, loading };
};

View file

@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import datemath from '@kbn/datemath';
export function callDateMath(value: unknown): number {
const DEFAULT_RETURN_VALUE = 0;
if (typeof value === 'string') {
return datemath.parse(value)?.valueOf() ?? DEFAULT_RETURN_VALUE;
}
return DEFAULT_RETURN_VALUE;
}

View file

@ -5,34 +5,28 @@
* 2.0.
*/
import { mergeProjection } from '../../projections/util/merge_projection';
import { SetupUX } from './route';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import {
TRANSACTION_DURATION,
TRANSACTION_URL,
} from '../../../common/elasticsearch_fieldnames';
import { SetupUX } from '../../../typings/ui_filters';
import { getRumPageLoadTransactionsProjection } from './projections';
import { callDateMath } from './call_date_math';
import { mergeProjection } from '../../../common/utils/merge_projection';
export async function getUrlSearch({
setup,
urlQuery,
percentile,
start,
end,
}: {
setup: SetupUX;
urlQuery?: string;
percentile: number;
start: number;
end: number;
}) {
export function urlSearchQuery(
restFilters: any,
uxQuery: any,
searchValue: string
) {
const setup: SetupUX = { uiFilters: restFilters ? restFilters : {} };
const projection = getRumPageLoadTransactionsProjection({
setup,
urlQuery,
start,
end,
urlQuery: searchValue,
...uxQuery,
start: callDateMath(uxQuery?.start),
end: callDateMath(uxQuery?.end),
});
const params = mergeProjection(projection, {
body: {
size: 0,
@ -51,7 +45,7 @@ export async function getUrlSearch({
medianPLD: {
percentiles: {
field: TRANSACTION_DURATION,
percents: [percentile],
percents: [Number(uxQuery?.percentile)],
},
},
},
@ -59,20 +53,6 @@ export async function getUrlSearch({
},
},
});
const { apmEventClient } = setup;
const response = await apmEventClient.search('get_url_search', params);
const { urls, totalUrls } = response.aggregations ?? {};
const pkey = percentile.toFixed(1);
return {
total: totalUrls?.value || 0,
items: (urls?.buckets ?? []).map((bucket) => ({
url: bucket.key as string,
count: bucket.doc_count,
pld: bucket.medianPLD.values[pkey] ?? 0,
})),
};
const { apm: _apm, ...rest } = params;
return rest;
}

View file

@ -1,93 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../common/ftr_provider_context';
export default function rumServicesApiTests({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('legacySupertestAsApmReadUser');
registry.when('CSM url search api without data', { config: 'trial', archives: [] }, () => {
it('returns empty list', async () => {
const response = await supertest.get('/internal/apm/ux/url-search').query({
start: '2020-09-07T20:35:54.654Z',
end: '2020-09-14T20:35:54.654Z',
uiFilters: '{"serviceName":["elastic-co-rum-test"]}',
percentile: 50,
});
expect(response.status).to.be(200);
expectSnapshot(response.body).toMatchInline(`
Object {
"items": Array [],
"total": 0,
}
`);
});
});
registry.when(
'CSM url search api with data',
{ config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] },
() => {
it('returns top urls when no query', async () => {
const response = await supertest.get('/internal/apm/ux/url-search').query({
start: '2020-09-07T20:35:54.654Z',
end: '2020-09-16T20:35:54.654Z',
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
percentile: 50,
});
expect(response.status).to.be(200);
expectSnapshot(response.body).toMatchInline(`
Object {
"items": Array [
Object {
"count": 5,
"pld": 4924000,
"url": "http://localhost:5601/nfw/app/csm?rangeFrom=now-15m&rangeTo=now&serviceName=kibana-frontend-8_0_0",
},
Object {
"count": 1,
"pld": 2760000,
"url": "http://localhost:5601/nfw/app/home",
},
],
"total": 2,
}
`);
});
it('returns specific results against query', async () => {
const response = await supertest.get('/internal/apm/ux/url-search').query({
start: '2020-09-07T20:35:54.654Z',
end: '2020-09-16T20:35:54.654Z',
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
urlQuery: 'csm',
percentile: 50,
});
expect(response.status).to.be(200);
expectSnapshot(response.body).toMatchInline(`
Object {
"items": Array [
Object {
"count": 5,
"pld": 4924000,
"url": "http://localhost:5601/nfw/app/csm?rangeFrom=now-15m&rangeTo=now&serviceName=kibana-frontend-8_0_0",
},
],
"total": 1,
}
`);
});
}
);
}