mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[CSM] Use stacked chart for page views (#78042)
This commit is contained in:
parent
99f652479a
commit
7544a33901
5 changed files with 403 additions and 31 deletions
|
@ -33,7 +33,10 @@ import { ChartWrapper } from '../ChartWrapper';
|
|||
import { I18LABELS } from '../translations';
|
||||
|
||||
interface Props {
|
||||
data?: Array<Record<string, number | null>>;
|
||||
data?: {
|
||||
topItems: string[];
|
||||
items: Array<Record<string, number | null>>;
|
||||
};
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
|
@ -68,15 +71,9 @@ export function PageViewsChart({ data, loading }: Props) {
|
|||
});
|
||||
};
|
||||
|
||||
let breakdownAccessors: Set<string> = new Set();
|
||||
if (data && data.length > 0) {
|
||||
data.forEach((item) => {
|
||||
breakdownAccessors = new Set([
|
||||
...Array.from(breakdownAccessors),
|
||||
...Object.keys(item).filter((key) => key !== 'x'),
|
||||
]);
|
||||
});
|
||||
}
|
||||
const breakdownAccessors = data?.topItems?.length ? data?.topItems : ['y'];
|
||||
|
||||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
|
||||
const customSeriesNaming: SeriesNameFn = ({ yAccessor }) => {
|
||||
if (yAccessor === 'y') {
|
||||
|
@ -86,8 +83,6 @@ export function PageViewsChart({ data, loading }: Props) {
|
|||
return yAccessor;
|
||||
};
|
||||
|
||||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
|
||||
return (
|
||||
<ChartWrapper loading={loading} height="250px">
|
||||
{(!loading || data) && (
|
||||
|
@ -115,7 +110,8 @@ export function PageViewsChart({ data, loading }: Props) {
|
|||
id="page_views"
|
||||
title={I18LABELS.pageViews}
|
||||
position={Position.Left}
|
||||
tickFormat={(d) => numeral(d).format('0a')}
|
||||
tickFormat={(d) => numeral(d).format('0')}
|
||||
labelFormat={(d) => numeral(d).format('0a')}
|
||||
/>
|
||||
<BarSeries
|
||||
id={I18LABELS.pageViews}
|
||||
|
@ -123,7 +119,8 @@ export function PageViewsChart({ data, loading }: Props) {
|
|||
yScaleType={ScaleType.Linear}
|
||||
xAccessor="x"
|
||||
yAccessors={Array.from(breakdownAccessors)}
|
||||
data={data ?? []}
|
||||
stackAccessors={['x']}
|
||||
data={data?.items ?? []}
|
||||
name={customSeriesNaming}
|
||||
/>
|
||||
</Chart>
|
||||
|
|
|
@ -51,6 +51,16 @@ export async function getPageViewTrends({
|
|||
}
|
||||
: undefined,
|
||||
},
|
||||
...(breakdownItem
|
||||
? {
|
||||
topBreakdowns: {
|
||||
terms: {
|
||||
field: breakdownItem.fieldName,
|
||||
size: 9,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -59,25 +69,44 @@ export async function getPageViewTrends({
|
|||
|
||||
const response = await apmEventClient.search(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 result.map((bucket) => {
|
||||
const { key: xVal, doc_count: bCount } = bucket;
|
||||
const res: Record<string, null | number> = {
|
||||
x: xVal,
|
||||
y: bCount,
|
||||
};
|
||||
if ('breakdown' in bucket) {
|
||||
const categoryBuckets = bucket.breakdown.buckets;
|
||||
categoryBuckets.forEach(({ key, doc_count: docCount }) => {
|
||||
if (key === 'Other') {
|
||||
res[key + `(${breakdownItem?.name})`] = docCount;
|
||||
} else {
|
||||
res[key] = docCount;
|
||||
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.Other = bCount - top9Count;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
return res;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
280
x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap
generated
Normal file
280
x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap
generated
Normal file
|
@ -0,0 +1,280 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CSM page views when there is 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[`CSM page views when there is 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[`CSM page views when there is no data returns empty list 1`] = `
|
||||
Object {
|
||||
"items": Array [],
|
||||
"topItems": Array [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CSM page views when there is no data returns empty list with breakdowns 1`] = `
|
||||
Object {
|
||||
"items": Array [],
|
||||
"topItems": Array [],
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { expectSnapshot } from '../../../common/match_snapshot';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
|
||||
export default function rumServicesApiTests({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('CSM page views', () => {
|
||||
describe('when there is no data', () => {
|
||||
it('returns empty list', async () => {
|
||||
const response = await supertest.get(
|
||||
'/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D'
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
it('returns empty list with breakdowns', async () => {
|
||||
const response = await supertest.get(
|
||||
'/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&breakdowns=%7B%22name%22%3A%22Browser%22%2C%22fieldName%22%3A%22user_agent.name%22%2C%22type%22%3A%22category%22%7D'
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is data', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('8.0.0');
|
||||
await esArchiver.load('rum_8.0.0');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('8.0.0');
|
||||
await esArchiver.unload('rum_8.0.0');
|
||||
});
|
||||
|
||||
it('returns page views', async () => {
|
||||
const response = await supertest.get(
|
||||
'/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D'
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
it('returns page views with breakdown', async () => {
|
||||
const response = await supertest.get(
|
||||
'/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&breakdowns=%7B%22name%22%3A%22Browser%22%2C%22fieldName%22%3A%22user_agent.name%22%2C%22type%22%3A%22category%22%7D'
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -35,6 +35,7 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr
|
|||
loadTestFile(require.resolve('./csm/csm_services.ts'));
|
||||
loadTestFile(require.resolve('./csm/web_core_vitals.ts'));
|
||||
loadTestFile(require.resolve('./csm/long_task_metrics.ts'));
|
||||
loadTestFile(require.resolve('./csm/page_views.ts'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue