mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Port page views migration to separate branch. (#134758)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
b26b73db54
commit
4e3ef399f9
25 changed files with 123 additions and 749 deletions
|
@ -270,51 +270,3 @@ Object {
|
|||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`rum client dashboard queries fetches page view trends 1`] = `
|
||||
Object {
|
||||
"apm": Object {
|
||||
"events": Array [
|
||||
"transaction",
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"aggs": Object {
|
||||
"pageViews": Object {
|
||||
"aggs": undefined,
|
||||
"auto_date_histogram": Object {
|
||||
"buckets": 50,
|
||||
"field": "@timestamp",
|
||||
},
|
||||
},
|
||||
},
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 50000,
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"transaction.type": "page-load",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"service.environment": "staging",
|
||||
},
|
||||
},
|
||||
],
|
||||
"must_not": Array [],
|
||||
},
|
||||
},
|
||||
"size": 0,
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -1,126 +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 { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
|
||||
import { mergeProjection } from '../../projections/util/merge_projection';
|
||||
import { SetupUX } from './route';
|
||||
|
||||
export interface BreakdownItem {
|
||||
name: string;
|
||||
type: string;
|
||||
fieldName: string;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export async function getPageViewTrends({
|
||||
setup,
|
||||
breakdowns,
|
||||
urlQuery,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
setup: SetupUX;
|
||||
breakdowns?: string;
|
||||
urlQuery?: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}) {
|
||||
const projection = getRumPageLoadTransactionsProjection({
|
||||
setup,
|
||||
urlQuery,
|
||||
checkFetchStartFieldExists: false,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
let breakdownItem: BreakdownItem | null = null;
|
||||
if (breakdowns) {
|
||||
breakdownItem = JSON.parse(breakdowns);
|
||||
}
|
||||
|
||||
const params = mergeProjection(projection, {
|
||||
body: {
|
||||
size: 0,
|
||||
query: {
|
||||
bool: projection.body.query.bool,
|
||||
},
|
||||
aggs: {
|
||||
pageViews: {
|
||||
auto_date_histogram: {
|
||||
field: '@timestamp',
|
||||
buckets: 50,
|
||||
},
|
||||
aggs: breakdownItem
|
||||
? {
|
||||
breakdown: {
|
||||
terms: {
|
||||
field: breakdownItem.fieldName,
|
||||
size: 9,
|
||||
missing: 'Others',
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...(breakdownItem
|
||||
? {
|
||||
topBreakdowns: {
|
||||
terms: {
|
||||
field: breakdownItem.fieldName,
|
||||
size: 9,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { apmEventClient } = setup;
|
||||
|
||||
const response = await apmEventClient.search('get_page_view_trends', params);
|
||||
|
||||
const { topBreakdowns } = response.aggregations ?? {};
|
||||
|
||||
// we are only displaying top 9
|
||||
const topItems: string[] = (topBreakdowns?.buckets ?? []).map(
|
||||
({ key }) => key as string
|
||||
);
|
||||
|
||||
const result = response.aggregations?.pageViews.buckets ?? [];
|
||||
|
||||
return {
|
||||
topItems,
|
||||
items: result.map((bucket) => {
|
||||
const { key: xVal, doc_count: bCount } = bucket;
|
||||
const res: Record<string, number> = {
|
||||
x: xVal,
|
||||
y: bCount,
|
||||
};
|
||||
if ('breakdown' in bucket) {
|
||||
let top9Count = 0;
|
||||
const categoryBuckets = bucket.breakdown.buckets;
|
||||
categoryBuckets.forEach(({ key, doc_count: docCount }) => {
|
||||
if (topItems.includes(key as string)) {
|
||||
if (res[key]) {
|
||||
// if term is already in object, just add it to it
|
||||
res[key] += docCount;
|
||||
} else {
|
||||
res[key] = docCount;
|
||||
}
|
||||
top9Count += docCount;
|
||||
}
|
||||
});
|
||||
// Top 9 plus others, get a diff from parent bucket total
|
||||
if (bCount > top9Count) {
|
||||
res.Others = bCount - top9Count;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}),
|
||||
};
|
||||
}
|
|
@ -9,7 +9,6 @@ import {
|
|||
SearchParamsMock,
|
||||
inspectSearchParams,
|
||||
} from '../../utils/test_helpers';
|
||||
import { getPageViewTrends } from './get_page_view_trends';
|
||||
import { getPageLoadDistribution } from './get_page_load_distribution';
|
||||
|
||||
describe('rum client dashboard queries', () => {
|
||||
|
@ -19,20 +18,6 @@ describe('rum client dashboard queries', () => {
|
|||
mock.teardown();
|
||||
});
|
||||
|
||||
it('fetches page view trends', async () => {
|
||||
mock = await inspectSearchParams(
|
||||
(setup) =>
|
||||
getPageViewTrends({
|
||||
setup,
|
||||
start: 0,
|
||||
end: 50000,
|
||||
}),
|
||||
{ uiFilters: { environment: 'staging' } }
|
||||
);
|
||||
|
||||
expect(mock.params).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fetches page load distribution', async () => {
|
||||
mock = await inspectSearchParams(
|
||||
(setup) =>
|
||||
|
|
|
@ -8,7 +8,6 @@ import * as t from 'io-ts';
|
|||
import { Logger } from '@kbn/core/server';
|
||||
import { setupRequest, Setup } from '../../lib/helpers/setup_request';
|
||||
import { getPageLoadDistribution } from './get_page_load_distribution';
|
||||
import { getPageViewTrends } from './get_page_view_trends';
|
||||
import { getPageLoadDistBreakdown } from './get_pl_dist_breakdown';
|
||||
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
|
||||
import { rangeRt } from '../default_api_types';
|
||||
|
@ -126,31 +125,6 @@ const rumPageLoadDistBreakdownRoute = createApmServerRoute({
|
|||
},
|
||||
});
|
||||
|
||||
const rumPageViewsTrendRoute = createApmServerRoute({
|
||||
endpoint: 'GET /internal/apm/ux/page-view-trends',
|
||||
params: t.type({
|
||||
query: t.intersection([uxQueryRt, t.partial({ breakdowns: t.string })]),
|
||||
}),
|
||||
options: { tags: ['access:apm'] },
|
||||
handler: async (
|
||||
resources
|
||||
): Promise<{ topItems: string[]; items: Array<Record<string, number>> }> => {
|
||||
const setup = await setupUXRequest(resources);
|
||||
|
||||
const {
|
||||
query: { breakdowns, urlQuery, start, end },
|
||||
} = resources.params;
|
||||
|
||||
return getPageViewTrends({
|
||||
setup,
|
||||
breakdowns,
|
||||
urlQuery,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function decodeUiFilters(
|
||||
logger: Logger,
|
||||
uiFiltersEncoded?: string
|
||||
|
@ -183,5 +157,4 @@ async function setupUXRequest<TParams extends SetupUXRequestParams>(
|
|||
export const rumRouteRepository = {
|
||||
...rumPageLoadDistributionRoute,
|
||||
...rumPageLoadDistBreakdownRoute,
|
||||
...rumPageViewsTrendRoute,
|
||||
};
|
||||
|
|
|
@ -169,3 +169,5 @@ export const PERCENTILE_RANKS = [
|
|||
];
|
||||
export const LABEL_FIELDS_FILTER = 'LABEL_FIELDS_FILTER';
|
||||
export const LABEL_FIELDS_BREAKDOWN = 'LABEL_FIELDS_BREAKDOWN';
|
||||
|
||||
export const ENVIRONMENT_ALL = 'ENVIRONMENT_ALL';
|
||||
|
|
|
@ -332,7 +332,7 @@ describe('Lens Attribute', () => {
|
|||
formulaHelper
|
||||
).getJSON() as any;
|
||||
expect(lensAttrWithMultiSeries.state.visualization.axisTitlesVisibilitySettings).toEqual({
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: false,
|
||||
yRight: false,
|
||||
});
|
||||
|
@ -345,7 +345,7 @@ describe('Lens Attribute', () => {
|
|||
formulaHelper
|
||||
).getJSON() as any;
|
||||
expect(lensAttrWithMultiSeries.state.visualization.axisTitlesVisibilitySettings).toEqual({
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
});
|
||||
|
@ -357,10 +357,10 @@ describe('Lens Attribute', () => {
|
|||
|
||||
it('should return expected XYState', function () {
|
||||
expect(lnsAttr.getXyState()).toEqual({
|
||||
axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
axisTitlesVisibilitySettings: { x: false, yLeft: true, yRight: true },
|
||||
curveType: 'CURVE_MONOTONE_X',
|
||||
fittingFunction: 'Linear',
|
||||
gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
gridlinesVisibilitySettings: { x: false, yLeft: true, yRight: true },
|
||||
layers: [
|
||||
{
|
||||
accessors: ['y-axis-column-layer0'],
|
||||
|
|
|
@ -876,12 +876,12 @@ export class LensAttributes {
|
|||
fittingFunction: 'Linear',
|
||||
curveType: 'CURVE_MONOTONE_X' as XYCurveType,
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: !this.isMultiSeries,
|
||||
yRight: !this.isMultiSeries,
|
||||
},
|
||||
tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
gridlinesVisibilitySettings: { x: false, yLeft: true, yRight: true },
|
||||
preferredSeriesType: 'line',
|
||||
layers: this.getDataLayers(),
|
||||
...(this.layerConfigs[0].seriesConfig.yTitle
|
||||
|
|
|
@ -61,15 +61,16 @@ export const testMobileKPIAttr = {
|
|||
valueLabels: 'hide',
|
||||
fittingFunction: 'Linear',
|
||||
curveType: 'CURVE_MONOTONE_X',
|
||||
axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
axisTitlesVisibilitySettings: { x: false, yLeft: true, yRight: true },
|
||||
tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true },
|
||||
gridlinesVisibilitySettings: { x: false, yLeft: true, yRight: true },
|
||||
preferredSeriesType: 'line',
|
||||
layers: [
|
||||
{
|
||||
accessors: ['y-axis-column-layer0'],
|
||||
layerId: 'layer0',
|
||||
layerType: 'data',
|
||||
palette: undefined,
|
||||
seriesType: 'line',
|
||||
yConfig: [{ forAccessor: 'y-axis-column-layer0', color: 'green', axisMode: 'left' }],
|
||||
xAccessor: 'x-axis-column-layer0',
|
||||
|
|
|
@ -98,6 +98,8 @@ export const sampleAttribute = {
|
|||
},
|
||||
scale: 'ratio',
|
||||
sourceField: RECORDS_FIELD,
|
||||
timeScale: undefined,
|
||||
timeShift: undefined,
|
||||
},
|
||||
'y-axis-column-layer0X1': {
|
||||
customLabel: true,
|
||||
|
@ -115,6 +117,8 @@ export const sampleAttribute = {
|
|||
},
|
||||
scale: 'ratio',
|
||||
sourceField: RECORDS_FIELD,
|
||||
timeScale: undefined,
|
||||
timeShift: undefined,
|
||||
},
|
||||
'y-axis-column-layer0X2': {
|
||||
customLabel: true,
|
||||
|
@ -122,6 +126,7 @@ export const sampleAttribute = {
|
|||
isBucketed: false,
|
||||
label: 'Part of Pages loaded',
|
||||
operationType: 'overall_sum',
|
||||
params: undefined,
|
||||
references: ['y-axis-column-layer0X1'],
|
||||
scale: 'ratio',
|
||||
},
|
||||
|
@ -232,14 +237,14 @@ export const sampleAttribute = {
|
|||
},
|
||||
visualization: {
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
curveType: 'CURVE_MONOTONE_X',
|
||||
fittingFunction: 'Linear',
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
|
@ -248,6 +253,7 @@ export const sampleAttribute = {
|
|||
accessors: ['y-axis-column-layer0'],
|
||||
layerId: 'layer0',
|
||||
layerType: 'data',
|
||||
palette: undefined,
|
||||
seriesType: 'line',
|
||||
xAccessor: 'x-axis-column-layer0',
|
||||
yConfig: [
|
||||
|
|
|
@ -102,14 +102,14 @@ export const sampleAttributeCoreWebVital = {
|
|||
},
|
||||
visualization: {
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
curveType: 'CURVE_MONOTONE_X',
|
||||
fittingFunction: 'Linear',
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
|
@ -118,6 +118,7 @@ export const sampleAttributeCoreWebVital = {
|
|||
accessors: ['y-axis-column-layer0', 'y-axis-column-1', 'y-axis-column-2'],
|
||||
layerId: 'layer0',
|
||||
layerType: 'data',
|
||||
palette: undefined,
|
||||
seriesType: 'bar_horizontal_percentage_stacked',
|
||||
xAccessor: 'x-axis-column-layer0',
|
||||
yConfig: [
|
||||
|
|
|
@ -63,14 +63,14 @@ export const sampleAttributeKpi = {
|
|||
},
|
||||
visualization: {
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
curveType: 'CURVE_MONOTONE_X',
|
||||
fittingFunction: 'Linear',
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
|
@ -79,6 +79,7 @@ export const sampleAttributeKpi = {
|
|||
accessors: ['y-axis-column-layer0'],
|
||||
layerId: 'layer0',
|
||||
layerType: 'data',
|
||||
palette: undefined,
|
||||
seriesType: 'line',
|
||||
xAccessor: 'x-axis-column-layer0',
|
||||
yConfig: [
|
||||
|
|
|
@ -98,6 +98,8 @@ export const sampleAttributeWithReferenceLines = {
|
|||
},
|
||||
scale: 'ratio',
|
||||
sourceField: RECORDS_FIELD,
|
||||
timeScale: undefined,
|
||||
timeShift: undefined,
|
||||
},
|
||||
'y-axis-column-layer0X1': {
|
||||
customLabel: true,
|
||||
|
@ -115,6 +117,8 @@ export const sampleAttributeWithReferenceLines = {
|
|||
},
|
||||
scale: 'ratio',
|
||||
sourceField: RECORDS_FIELD,
|
||||
timeScale: undefined,
|
||||
timeShift: undefined,
|
||||
},
|
||||
'y-axis-column-layer0X2': {
|
||||
customLabel: true,
|
||||
|
@ -122,6 +126,7 @@ export const sampleAttributeWithReferenceLines = {
|
|||
isBucketed: false,
|
||||
label: 'Part of Pages loaded',
|
||||
operationType: 'overall_sum',
|
||||
params: undefined,
|
||||
references: ['y-axis-column-layer0X1'],
|
||||
scale: 'ratio',
|
||||
},
|
||||
|
@ -232,14 +237,14 @@ export const sampleAttributeWithReferenceLines = {
|
|||
},
|
||||
visualization: {
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
curveType: 'CURVE_MONOTONE_X',
|
||||
fittingFunction: 'Linear',
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
x: false,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
|
@ -248,6 +253,7 @@ export const sampleAttributeWithReferenceLines = {
|
|||
accessors: ['y-axis-column-layer0'],
|
||||
layerId: 'layer0',
|
||||
layerType: 'data',
|
||||
palette: undefined,
|
||||
seriesType: 'line',
|
||||
xAccessor: 'x-axis-column-layer0',
|
||||
yConfig: [
|
||||
|
|
|
@ -104,6 +104,7 @@ export type { SeriesConfig, ConfigProps } from './components/shared/exploratory_
|
|||
export {
|
||||
ReportTypes,
|
||||
FILTER_RECORDS,
|
||||
ENVIRONMENT_ALL,
|
||||
REPORT_METRIC_FIELD,
|
||||
USE_BREAK_DOWN_COLUMN,
|
||||
RECORDS_FIELD,
|
||||
|
|
|
@ -84,7 +84,6 @@ export function BreakdownFilter({
|
|||
|
||||
return (
|
||||
<EuiSuperSelect
|
||||
fullWidth
|
||||
compressed
|
||||
options={options}
|
||||
valueOfSelected={selectedBreakdown?.fieldName ?? NO_BREAKDOWN}
|
||||
|
|
|
@ -32,9 +32,9 @@ import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
|
|||
import { PercentileAnnotations } from '../page_load_distribution/percentile_annotations';
|
||||
import { I18LABELS } from '../translations';
|
||||
import { ChartWrapper } from '../chart_wrapper';
|
||||
import { PercentileRange } from '../page_load_distribution';
|
||||
import { BreakdownSeries } from '../page_load_distribution/breakdown_series';
|
||||
import { BreakdownItem } from '../../../../../typings/ui_filters';
|
||||
import { PercentileRange } from '../page_load_distribution/types';
|
||||
|
||||
interface PageLoadData {
|
||||
pageLoadDistribution: Array<{ x: number; y: number }>;
|
||||
|
|
|
@ -5,59 +5,79 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import {
|
||||
Axis,
|
||||
BarSeries,
|
||||
BrushEndListener,
|
||||
Chart,
|
||||
DARK_THEME,
|
||||
LIGHT_THEME,
|
||||
niceTimeFormatByDay,
|
||||
ScaleType,
|
||||
SeriesNameFn,
|
||||
Settings,
|
||||
timeFormatter,
|
||||
Position,
|
||||
} from '@elastic/charts';
|
||||
AllSeries,
|
||||
ALL_VALUES_SELECTED,
|
||||
fromQuery,
|
||||
RECORDS_FIELD,
|
||||
toQuery,
|
||||
} from '@kbn/observability-plugin/public';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
EUI_CHARTS_THEME_DARK,
|
||||
EUI_CHARTS_THEME_LIGHT,
|
||||
} from '@elastic/eui/dist/eui_charts_theme';
|
||||
import numeral from '@elastic/numeral';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
|
||||
import { fromQuery, toQuery } from '@kbn/observability-plugin/public';
|
||||
|
||||
import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values';
|
||||
import { BreakdownItem, UxUIFilters } from '../../../../../typings/ui_filters';
|
||||
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
|
||||
import { ChartWrapper } from '../chart_wrapper';
|
||||
import { I18LABELS } from '../translations';
|
||||
import { useKibanaServices } from '../../../../hooks/use_kibana_services';
|
||||
import { useDataView } from '../local_uifilters/use_data_view';
|
||||
import {
|
||||
SERVICE_ENVIRONMENT,
|
||||
SERVICE_NAME,
|
||||
} from '../../../../../common/elasticsearch_fieldnames';
|
||||
|
||||
interface Props {
|
||||
data?: {
|
||||
topItems: string[];
|
||||
items: Array<Record<string, number | null>>;
|
||||
};
|
||||
loading: boolean;
|
||||
breakdown: BreakdownItem | null;
|
||||
uiFilters: UxUIFilters;
|
||||
}
|
||||
|
||||
export function PageViewsChart({ data, loading }: Props) {
|
||||
export function PageViewsChart({ breakdown, uiFilters }: Props) {
|
||||
const { dataViewTitle } = useDataView();
|
||||
const history = useHistory();
|
||||
const { urlParams } = useLegacyUrlParams();
|
||||
|
||||
const kibana = useKibanaServices();
|
||||
const { ExploratoryViewEmbeddable } = kibana.observability;
|
||||
const { start, end } = urlParams;
|
||||
const diffInDays = moment(new Date(end as string)).diff(
|
||||
moment(new Date(start as string)),
|
||||
'day'
|
||||
);
|
||||
|
||||
const formatter = timeFormatter(niceTimeFormatByDay(diffInDays > 1 ? 2 : 1));
|
||||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
|
||||
const onBrushEnd: BrushEndListener = ({ x }) => {
|
||||
if (!x) {
|
||||
const euiChartTheme = darkMode
|
||||
? EUI_CHARTS_THEME_DARK
|
||||
: EUI_CHARTS_THEME_LIGHT;
|
||||
|
||||
const allSeries: AllSeries = [
|
||||
{
|
||||
dataType: 'ux',
|
||||
time: {
|
||||
from: start ?? '',
|
||||
to: end ?? '',
|
||||
},
|
||||
name: 'ux-series-1',
|
||||
selectedMetricField: RECORDS_FIELD,
|
||||
reportDefinitions: {
|
||||
[SERVICE_ENVIRONMENT]:
|
||||
!uiFilters?.environment ||
|
||||
uiFilters.environment === ENVIRONMENT_ALL.value
|
||||
? [ALL_VALUES_SELECTED]
|
||||
: [uiFilters.environment],
|
||||
[SERVICE_NAME]: urlParams.serviceName
|
||||
? [urlParams.serviceName]
|
||||
: [ALL_VALUES_SELECTED],
|
||||
},
|
||||
breakdown: breakdown?.fieldName,
|
||||
color: euiChartTheme.theme.colors?.vizColors?.[1],
|
||||
},
|
||||
];
|
||||
const onBrushEnd = ({ range }: { range: number[] }) => {
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
const [minX, maxX] = x;
|
||||
const [minX, maxX] = range;
|
||||
|
||||
const rangeFrom = moment(minX).toISOString();
|
||||
const rangeTo = moment(maxX).toISOString();
|
||||
|
@ -72,67 +92,15 @@ export function PageViewsChart({ data, loading }: Props) {
|
|||
});
|
||||
};
|
||||
|
||||
const hasBreakdowns = !!data?.topItems?.length;
|
||||
|
||||
const breakdownAccessors = data?.topItems?.length ? data?.topItems : ['y'];
|
||||
|
||||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
|
||||
const customSeriesNaming: SeriesNameFn = ({ yAccessor }) => {
|
||||
if (yAccessor === 'y') {
|
||||
return I18LABELS.overall;
|
||||
}
|
||||
|
||||
return yAccessor;
|
||||
};
|
||||
|
||||
const euiChartTheme = darkMode
|
||||
? EUI_CHARTS_THEME_DARK
|
||||
: EUI_CHARTS_THEME_LIGHT;
|
||||
|
||||
return (
|
||||
<ChartWrapper loading={loading} height="250px">
|
||||
{(!loading || data) && (
|
||||
<Chart>
|
||||
<Settings
|
||||
baseTheme={darkMode ? DARK_THEME : LIGHT_THEME}
|
||||
theme={euiChartTheme.theme}
|
||||
showLegend
|
||||
onBrushEnd={onBrushEnd}
|
||||
xDomain={{
|
||||
min: new Date(start as string).valueOf(),
|
||||
max: new Date(end as string).valueOf(),
|
||||
}}
|
||||
/>
|
||||
<Axis
|
||||
id="date_time"
|
||||
position={Position.Bottom}
|
||||
tickFormat={formatter}
|
||||
/>
|
||||
<Axis
|
||||
id="page_views"
|
||||
title={I18LABELS.pageViews}
|
||||
position={Position.Left}
|
||||
tickFormat={(d) => numeral(d).format('0')}
|
||||
labelFormat={(d) => numeral(d).format('0a')}
|
||||
/>
|
||||
<BarSeries
|
||||
id={I18LABELS.pageViews}
|
||||
xScaleType={ScaleType.Time}
|
||||
yScaleType={ScaleType.Linear}
|
||||
xAccessor="x"
|
||||
yAccessors={Array.from(breakdownAccessors)}
|
||||
stackAccessors={['x']}
|
||||
data={data?.items ?? []}
|
||||
name={customSeriesNaming}
|
||||
color={
|
||||
!hasBreakdowns
|
||||
? euiChartTheme.theme.colors?.vizColors?.[1]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Chart>
|
||||
)}
|
||||
</ChartWrapper>
|
||||
<ExploratoryViewEmbeddable
|
||||
customHeight="300px"
|
||||
attributes={allSeries}
|
||||
onBrushEnd={onBrushEnd}
|
||||
reportType="kpi-over-time"
|
||||
dataTypesIndexPatterns={{ ux: dataViewTitle }}
|
||||
isSingleMetric={true}
|
||||
axisTitlesVisibility={{ x: false, yRight: true, yLeft: true }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
EUI_CHARTS_THEME_LIGHT,
|
||||
} from '@elastic/eui/dist/eui_charts_theme';
|
||||
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
|
||||
import { PercentileRange } from '.';
|
||||
import { PercentileRange } from './types';
|
||||
import { useBreakdowns } from './use_breakdowns';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -23,11 +23,7 @@ import { PageLoadDistChart } from '../charts/page_load_dist_chart';
|
|||
import { ResetPercentileZoom } from './reset_percentile_zoom';
|
||||
import { useKibanaServices } from '../../../../hooks/use_kibana_services';
|
||||
import { BreakdownItem } from '../../../../../typings/ui_filters';
|
||||
|
||||
export interface PercentileRange {
|
||||
min?: number | null;
|
||||
max?: number | null;
|
||||
}
|
||||
import { PercentileRange } from './types';
|
||||
|
||||
export function PageLoadDistribution() {
|
||||
const { http } = useKibanaServices();
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
import { I18LABELS } from '../translations';
|
||||
import { PercentileRange } from '.';
|
||||
import { PercentileRange } from './types';
|
||||
|
||||
interface Props {
|
||||
percentileRange: PercentileRange;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export interface PercentileRange {
|
||||
min?: number | null;
|
||||
max?: number | null;
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
|
||||
import { PercentileRange } from '.';
|
||||
import { PercentileRange } from './types';
|
||||
|
||||
interface Props {
|
||||
percentileRange?: PercentileRange;
|
||||
|
|
|
@ -6,59 +6,27 @@
|
|||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { createExploratoryViewUrl } from '@kbn/observability-plugin/public';
|
||||
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
|
||||
import { useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { I18LABELS } from '../translations';
|
||||
import { BreakdownFilter } from '../breakdowns/breakdown_filter';
|
||||
import { PageViewsChart } from '../charts/page_views_chart';
|
||||
import { useKibanaServices } from '../../../../hooks/use_kibana_services';
|
||||
import { BreakdownItem } from '../../../../../typings/ui_filters';
|
||||
import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames';
|
||||
|
||||
export function PageViewsTrend() {
|
||||
const { http } = useKibanaServices();
|
||||
|
||||
const { rangeId, urlParams, uxUiFilters } = useLegacyUrlParams();
|
||||
const { urlParams, uxUiFilters } = useLegacyUrlParams();
|
||||
const { serviceName } = uxUiFilters;
|
||||
|
||||
const { start, end, searchTerm, rangeTo, rangeFrom } = urlParams;
|
||||
const { rangeTo, rangeFrom } = urlParams;
|
||||
|
||||
const [breakdown, setBreakdown] = useState<BreakdownItem | null>(null);
|
||||
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (start && end && serviceName) {
|
||||
return callApmApi('GET /internal/apm/ux/page-view-trends', {
|
||||
params: {
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
uiFilters: JSON.stringify(uxUiFilters),
|
||||
urlQuery: searchTerm,
|
||||
...(breakdown
|
||||
? {
|
||||
breakdowns: JSON.stringify(breakdown),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
// `rangeId` acts as a cache buster for stable ranges like "Today"
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[start, end, serviceName, uxUiFilters, searchTerm, breakdown, rangeId]
|
||||
);
|
||||
|
||||
const exploratoryViewLink = createExploratoryViewUrl(
|
||||
{
|
||||
reportType: 'kpi-over-time',
|
||||
|
@ -68,7 +36,7 @@ export function PageViewsTrend() {
|
|||
dataType: 'ux',
|
||||
time: { from: rangeFrom!, to: rangeTo! },
|
||||
reportDefinitions: {
|
||||
'service.name': serviceName as string[],
|
||||
[SERVICE_NAME]: serviceName as string[],
|
||||
},
|
||||
...(breakdown ? { breakdown: breakdown.fieldName } : {}),
|
||||
},
|
||||
|
@ -80,7 +48,7 @@ export function PageViewsTrend() {
|
|||
const showAnalyzeButton = false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<EuiFlexGroup responsive={false}>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
|
@ -109,8 +77,7 @@ export function PageViewsTrend() {
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<PageViewsChart data={data} loading={status !== 'success'} />
|
||||
</div>
|
||||
<PageViewsChart breakdown={breakdown} uiFilters={uxUiFilters} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export function PageLoadAndViews() {
|
|||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem style={{ flexBasis: 650 }}>
|
||||
<EuiPanel hasBorder={true}>
|
||||
<EuiPanel hasBorder={true} style={{ paddingBottom: 0 }}>
|
||||
<PageViewsTrend />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -1,280 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`APM API tests trial 8.0.0,rum_8.0.0 CSM page views with data returns page views 1`] = `
|
||||
Object {
|
||||
"items": Array [
|
||||
Object {
|
||||
"x": 1600149947000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149957000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149967000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149977000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149987000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149997000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150007000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150017000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150027000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150037000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150047000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150057000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150067000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150077000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150087000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150097000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150107000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150117000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150127000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150137000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150147000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150157000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150167000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150177000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150187000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150197000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150207000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150217000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150227000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150237000,
|
||||
"y": 1,
|
||||
},
|
||||
],
|
||||
"topItems": Array [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`APM API tests trial 8.0.0,rum_8.0.0 CSM page views with data returns page views with breakdown 1`] = `
|
||||
Object {
|
||||
"items": Array [
|
||||
Object {
|
||||
"Chrome": 1,
|
||||
"x": 1600149947000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149957000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149967000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149977000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149987000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600149997000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150007000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150017000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"Chrome": 1,
|
||||
"x": 1600150027000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150037000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150047000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150057000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150067000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"Chrome": 1,
|
||||
"x": 1600150077000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150087000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150097000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150107000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150117000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150127000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150137000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150147000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150157000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150167000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"Chrome": 1,
|
||||
"x": 1600150177000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150187000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150197000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"Chrome Mobile": 1,
|
||||
"x": 1600150207000,
|
||||
"y": 1,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150217000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1600150227000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"Chrome Mobile": 1,
|
||||
"x": 1600150237000,
|
||||
"y": 1,
|
||||
},
|
||||
],
|
||||
"topItems": Array [
|
||||
"Chrome",
|
||||
"Chrome Mobile",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`APM API tests trial no data CSM page views without data returns empty list 1`] = `
|
||||
Object {
|
||||
"items": Array [],
|
||||
"topItems": Array [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`APM API tests trial no data CSM page views without data returns empty list with breakdowns 1`] = `
|
||||
Object {
|
||||
"items": Array [],
|
||||
"topItems": Array [],
|
||||
}
|
||||
`;
|
|
@ -1,89 +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 apmApiClient = getService('apmApiClient');
|
||||
|
||||
registry.when('CSM page views without data', { config: 'trial', archives: [] }, () => {
|
||||
it('returns empty list', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/ux/page-view-trends',
|
||||
params: {
|
||||
query: {
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-14T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["elastic-co-rum-test"]}',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
|
||||
it('returns empty list with breakdowns', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/ux/page-view-trends',
|
||||
params: {
|
||||
query: {
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-14T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["elastic-co-rum-test"]}',
|
||||
breakdowns: '{"name":"Browser","fieldName":"user_agent.name","type":"category"}',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
});
|
||||
|
||||
registry.when(
|
||||
'CSM page views with data',
|
||||
{ config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] },
|
||||
() => {
|
||||
it('returns page views', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/ux/page-view-trends',
|
||||
params: {
|
||||
query: {
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-16T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
it('returns page views with breakdown', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/ux/page-view-trends',
|
||||
params: {
|
||||
query: {
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-16T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
|
||||
breakdowns: '{"name":"Browser","fieldName":"user_agent.name","type":"category"}',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue