[CSM] Use stacked chart for page views (#78042)

This commit is contained in:
Shahzad 2020-09-22 18:00:19 +02:00 committed by GitHub
parent 99f652479a
commit 7544a33901
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 403 additions and 31 deletions

View file

@ -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>

View file

@ -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;
}),
};
}

View 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 [],
}
`;

View file

@ -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();
});
});
});
}

View file

@ -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'));
});
});
}