Skip to content

Commit

Permalink
Refactor o11y overview registration to not rely on deleted APM route.
Browse files Browse the repository at this point in the history
  • Loading branch information
justinkambic committed Jun 7, 2022
1 parent d09d5a2 commit b96effc
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,91 @@
* 2.0.
*/

import { ESSearchResponse } from '@kbn/core/types/elasticsearch';
import {
DataPublicPluginStart,
isCompleteResponse,
} from '@kbn/data-plugin/public';
import {
FetchDataParams,
HasDataParams,
UxFetchDataResponse,
UXHasDataResponse,
UXMetrics,
} from '@kbn/observability-plugin/public';
import {
coreWebVitalsQuery,
transformCoreWebVitalsResponse,
DEFAULT_RANKS,
} from '../../../services/data/core_web_vitals_query';
import { callApmApi } from '../../../services/rest/create_call_apm_api';

export { createCallApmApi } from '../../../services/rest/create_call_apm_api';

export const fetchUxOverviewDate = async ({
type FetchUxOverviewDateParams = FetchDataParams & {
dataStartPlugin: DataPublicPluginStart;
};

async function getCoreWebVitalsResponse({
absoluteTime,
relativeTime,
serviceName,
}: FetchDataParams): Promise<UxFetchDataResponse> => {
const data = await callApmApi('GET /internal/apm/ux/web-core-vitals', {
dataStartPlugin,
}: FetchUxOverviewDateParams) {
const dataView = await callApmApi('GET /internal/apm/data_view/dynamic', {
signal: null,
params: {
query: {
start: new Date(absoluteTime.start).toISOString(),
end: new Date(absoluteTime.end).toISOString(),
uiFilters: `{"serviceName":["${serviceName}"]}`,
},
},
});
return new Promise<
ESSearchResponse<{}, ReturnType<typeof coreWebVitalsQuery>>
>((resolve) => {
const search$ = dataStartPlugin.search
.search(
{
params: {
index: dataView.dynamicDataView?.title,
...coreWebVitalsQuery(
absoluteTime.start,
absoluteTime.end,
undefined,
{
serviceName: serviceName ? [serviceName] : undefined,
}
),
},
},
{}
)
.subscribe({
next: (result) => {
if (isCompleteResponse(result)) {
resolve(result.rawResponse as any);
search$.unsubscribe();
}
},
});
});
}

const CORE_WEB_VITALS_DEFAULTS: UXMetrics = {
coreVitalPages: 0,
cls: 0,
fid: 0,
lcp: 0,
tbt: 0,
fcp: 0,
lcpRanks: DEFAULT_RANKS,
fidRanks: DEFAULT_RANKS,
clsRanks: DEFAULT_RANKS,
};

export const fetchUxOverviewDate = async (
params: FetchUxOverviewDateParams
): Promise<UxFetchDataResponse> => {
const coreWebVitalsResponse = await getCoreWebVitalsResponse(params);
return {
coreWebVitals: data,
appLink: `/app/ux?rangeFrom=${relativeTime.start}&rangeTo=${relativeTime.end}`,
coreWebVitals:
transformCoreWebVitalsResponse(coreWebVitalsResponse) ??
CORE_WEB_VITALS_DEFAULTS,
appLink: `/app/ux?rangeFrom=${params.relativeTime.start}&rangeTo=${params.relativeTime.end}`,
};
};

Expand Down
54 changes: 5 additions & 49 deletions x-pack/plugins/ux/public/hooks/use_core_web_vitals_query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,11 @@ import { useDataView } from '../components/app/rum_dashboard/local_uifilters/use
import { callDateMath } from '../services/data/call_date_math';
import {
coreWebVitalsQuery,
transformCoreWebVitalsResponse,
PERCENTILE_DEFAULT,
} from '../services/data/core_web_vitals_query';
import { useUxQuery } from '../components/app/rum_dashboard/hooks/use_ux_query';

const getRanksPercentages = (ranks?: Record<string, number | null>) => {
if (!Array.isArray(ranks)) return null;
const ranksVal = ranks?.map(({ value }) => value?.toFixed(0) ?? 0) ?? [];
return [
Number(ranksVal?.[0]),
Number(ranksVal?.[1]) - Number(ranksVal?.[0]),
100 - Number(ranksVal?.[1]),
];
};

export function useCoreWebVitalsQuery(uxQuery: ReturnType<typeof useUxQuery>) {
const { dataViewTitle } = useDataView();
const { data: esQueryResponse, loading } = useEsSearch(
Expand All @@ -42,45 +33,10 @@ export function useCoreWebVitalsQuery(uxQuery: ReturnType<typeof useUxQuery>) {
{ name: 'UxCoreWebVitals' }
);
const data = useMemo(() => {
if (!esQueryResponse) return esQueryResponse;
const {
lcp,
cls,
fid,
tbt,
fcp,
lcpRanks,
fidRanks,
clsRanks,
coreVitalPages,
} = esQueryResponse.aggregations ?? {};

const defaultRanks = [100, 0, 0];

const pkey = (
!!uxQuery?.percentile ? Number(uxQuery?.percentile) : PERCENTILE_DEFAULT
).toFixed(1);

return {
coreVitalPages: coreVitalPages?.doc_count ?? 0,
/* Because cls is required in the type UXMetrics, and defined as number | null,
* we need to default to null in the case where cls is undefined in order to satisfy the UXMetrics type */
cls: cls?.values[pkey] ?? null,
fid: fid?.values[pkey],
lcp: lcp?.values[pkey],
tbt: tbt?.values[pkey] ?? 0,
fcp: fcp?.values[pkey],

lcpRanks: lcp?.values[pkey]
? getRanksPercentages(lcpRanks?.values) ?? defaultRanks
: defaultRanks,
fidRanks: fid?.values[pkey]
? getRanksPercentages(fidRanks?.values) ?? defaultRanks
: defaultRanks,
clsRanks: cls?.values[pkey]
? getRanksPercentages(clsRanks?.values) ?? defaultRanks
: defaultRanks,
};
transformCoreWebVitalsResponse(
esQueryResponse,
uxQuery?.percentile ? Number(uxQuery?.percentile) : PERCENTILE_DEFAULT
);
}, [esQueryResponse, uxQuery?.percentile]);
return { data, loading };
}
8 changes: 7 additions & 1 deletion x-pack/plugins/ux/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,14 @@ export class UxPlugin implements Plugin<UxPluginSetup, UxPluginStart> {
return await dataHelper.hasRumData(params!);
},
fetchData: async (params: FetchDataParams) => {
const [_, startPlugins] = await core.getStartServices();

const { data: dataStartPlugin } = startPlugins as ApmPluginStartDeps;
const dataHelper = await getUxDataHelper();
return await dataHelper.fetchUxOverviewDate(params);
return await dataHelper.fetchUxOverviewDate({
...params,
dataStartPlugin,
});
},
});
}
Expand Down
54 changes: 54 additions & 0 deletions x-pack/plugins/ux/public/services/data/core_web_vitals_query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { ESSearchResponse } from '@kbn/core/types/elasticsearch';
import {
TBT_FIELD,
FCP_FIELD,
Expand All @@ -16,6 +17,59 @@ import { SetupUX, UxUIFilters } from '../../../typings/ui_filters';
import { mergeProjection } from '../../../common/utils/merge_projection';
import { getRumPageLoadTransactionsProjection } from './projections';

export const DEFAULT_RANKS = [100, 0, 0];

const getRanksPercentages = (ranks?: Record<string, number | null>) => {
if (!Array.isArray(ranks)) return null;
const ranksVal = ranks?.map(({ value }) => value?.toFixed(0) ?? 0) ?? [];
return [
Number(ranksVal?.[0]),
Number(ranksVal?.[1]) - Number(ranksVal?.[0]),
100 - Number(ranksVal?.[1]),
];
};

export function transformCoreWebVitalsResponse<T>(
response?: ESSearchResponse<T, ReturnType<typeof coreWebVitalsQuery>>,
percentile = PERCENTILE_DEFAULT
) {
if (!response) return response;
const {
lcp,
cls,
fid,
tbt,
fcp,
lcpRanks,
fidRanks,
clsRanks,
coreVitalPages,
} = response.aggregations ?? {};

const pkey = percentile.toFixed(1);

return {
coreVitalPages: coreVitalPages?.doc_count ?? 0,
/* Because cls is required in the type UXMetrics, and defined as number | null,
* we need to default to null in the case where cls is undefined in order to satisfy the UXMetrics type */
cls: cls?.values[pkey] ?? null,
fid: fid?.values[pkey],
lcp: lcp?.values[pkey],
tbt: tbt?.values[pkey] ?? 0,
fcp: fcp?.values[pkey],

lcpRanks: lcp?.values[pkey]
? getRanksPercentages(lcpRanks?.values) ?? DEFAULT_RANKS
: DEFAULT_RANKS,
fidRanks: fid?.values[pkey]
? getRanksPercentages(fidRanks?.values) ?? DEFAULT_RANKS
: DEFAULT_RANKS,
clsRanks: cls?.values[pkey]
? getRanksPercentages(clsRanks?.values) ?? DEFAULT_RANKS
: DEFAULT_RANKS,
};
}

export const PERCENTILE_DEFAULT = 50;

export function coreWebVitalsQuery(
Expand Down

0 comments on commit b96effc

Please sign in to comment.