Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [UX] Add percentile selector (#78562) #79058

Merged
merged 1 commit into from
Oct 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions x-pack/plugins/apm/common/ui_filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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 { i18n } from '@kbn/i18n';
import {
AGENT_NAME,
CLIENT_GEO_COUNTRY_ISO_CODE,
CONTAINER_ID,
HOST_NAME,
POD_NAME,
SERVICE_NAME,
SERVICE_VERSION,
TRANSACTION_RESULT,
TRANSACTION_URL,
USER_AGENT_DEVICE,
USER_AGENT_NAME,
USER_AGENT_OS,
} from './elasticsearch_fieldnames';

export const filtersByName = {
host: {
title: i18n.translate('xpack.apm.localFilters.titles.host', {
defaultMessage: 'Host',
}),
fieldName: HOST_NAME,
},
agentName: {
title: i18n.translate('xpack.apm.localFilters.titles.agentName', {
defaultMessage: 'Agent name',
}),
fieldName: AGENT_NAME,
},
containerId: {
title: i18n.translate('xpack.apm.localFilters.titles.containerId', {
defaultMessage: 'Container ID',
}),
fieldName: CONTAINER_ID,
},
podName: {
title: i18n.translate('xpack.apm.localFilters.titles.podName', {
defaultMessage: 'Kubernetes pod',
}),
fieldName: POD_NAME,
},
transactionResult: {
title: i18n.translate('xpack.apm.localFilters.titles.transactionResult', {
defaultMessage: 'Transaction result',
}),
fieldName: TRANSACTION_RESULT,
},
serviceVersion: {
title: i18n.translate('xpack.apm.localFilters.titles.serviceVersion', {
defaultMessage: 'Service version',
}),
fieldName: SERVICE_VERSION,
},
transactionUrl: {
title: i18n.translate('xpack.apm.localFilters.titles.transactionUrl', {
defaultMessage: 'Url',
}),
fieldName: TRANSACTION_URL,
},
browser: {
title: i18n.translate('xpack.apm.localFilters.titles.browser', {
defaultMessage: 'Browser',
}),
fieldName: USER_AGENT_NAME,
},
device: {
title: i18n.translate('xpack.apm.localFilters.titles.device', {
defaultMessage: 'Device',
}),
fieldName: USER_AGENT_DEVICE,
},
location: {
title: i18n.translate('xpack.apm.localFilters.titles.location', {
defaultMessage: 'Location',
}),
fieldName: CLIENT_GEO_COUNTRY_ISO_CODE,
},
os: {
title: i18n.translate('xpack.apm.localFilters.titles.os', {
defaultMessage: 'OS',
}),
fieldName: USER_AGENT_OS,
},
serviceName: {
title: i18n.translate('xpack.apm.localFilters.titles.serviceName', {
defaultMessage: 'Service name',
}),
fieldName: SERVICE_NAME,
},
};

export type LocalUIFilterName = keyof typeof filtersByName;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Feature: CSM Dashboard
When a user browses the APM UI application for RUM Data
Then should have correct client metrics

Scenario: Percentile select
When the user changes the selected percentile
Then it displays client metric related to that percentile

Scenario Outline: CSM page filters
When the user filters by "<filterName>"
Then it filters the client metrics "<filterName>"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { When, Then } from 'cypress-cucumber-preprocessor/steps';
import { verifyClientMetrics } from './client_metrics_helper';
import { getDataTestSubj } from './utils';

When('the user changes the selected percentile', () => {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');

getDataTestSubj('uxPercentileSelect').click();

getDataTestSubj('p95Percentile').click();
});

Then(`it displays client metric related to that percentile`, () => {
const metrics = ['14 ms', '0.13 s', '55 '];

verifyClientMetrics(metrics, false);

// reset to median
getDataTestSubj('uxPercentileSelect').click();

getDataTestSubj('p50Percentile').click();
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Then(`it displays top pages in the suggestion popover`, () => {
listOfUrls.should('have.length', 5);

const actualUrlsText = [
'http://opbeans-node:3000/dashboardPage views: 17Page load duration: 109 ms ',
'http://opbeans-node:3000/dashboardPage views: 17Page load duration: 109 ms',
'http://opbeans-node:3000/ordersPage views: 14Page load duration: 72 ms',
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { DEFAULT_TIMEOUT } from './csm_dashboard';

export function getDataTestSubj(dataTestSubj: string) {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');

return cy.get(`[data-test-subj=${dataTestSubj}]`, DEFAULT_TIMEOUT);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import styled from 'styled-components';
import { useContext, useEffect } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiToolTip } from '@elastic/eui';
import { useFetcher } from '../../../../hooks/useFetcher';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { I18LABELS } from '../translations';
import { useUxQuery } from '../hooks/useUxQuery';
import { CsmSharedContext } from '../CsmSharedContext';

const ClFlexGroup = styled(EuiFlexGroup)`
Expand All @@ -22,29 +22,23 @@ const ClFlexGroup = styled(EuiFlexGroup)`
`;

export function ClientMetrics() {
const { urlParams, uiFilters } = useUrlParams();

const { start, end, searchTerm } = urlParams;
const uxQuery = useUxQuery();

const { data, status } = useFetcher(
(callApmApi) => {
const { serviceName } = uiFilters;
if (start && end && serviceName) {
if (uxQuery) {
return callApmApi({
pathname: '/api/apm/rum/client-metrics',
params: {
query: {
start,
end,
uiFilters: JSON.stringify(uiFilters),
urlQuery: searchTerm,
...uxQuery,
},
},
});
}
return Promise.resolve(null);
},
[start, end, uiFilters, searchTerm]
[uxQuery]
);

const { setSharedData } = useContext(CsmSharedContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export function PageLoadDistribution() {

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
const { serviceName } = uiFilters;

if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/page-load-distribution',
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export function PageViewsTrend() {

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
const { serviceName } = uiFilters;

if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/page-view-trends',
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { RumOverview } from '../RumDashboard';
import { RumHeader } from './RumHeader';
import { UserPercentile } from './UserPercentile';
import { CsmSharedContextProvider } from './CsmSharedContext';

export const UX_LABEL = i18n.translate('xpack.apm.ux.title', {
Expand All @@ -21,11 +22,14 @@ export function RumHome() {
<CsmSharedContextProvider>
<RumHeader>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexItem grow={true}>
<EuiTitle size="l">
<h1>{UX_LABEL}</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<UserPercentile />
</EuiFlexItem>
</EuiFlexGroup>
</RumHeader>
<RumOverview />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FormEvent, useRef, useState } from 'react';
import React, { FormEvent, SetStateAction, useRef, useState } from 'react';
import {
EuiButtonEmpty,
EuiFlexGroup,
Expand Down Expand Up @@ -33,6 +33,8 @@ interface Props {
onChange: (updatedOptions: UrlOption[]) => void;
searchValue: string;
onClose: () => void;
popoverIsOpen: boolean;
setPopoverIsOpen: React.Dispatch<SetStateAction<boolean>>;
}

export function SelectableUrlList({
Expand All @@ -43,8 +45,9 @@ export function SelectableUrlList({
onChange,
searchValue,
onClose,
popoverIsOpen,
setPopoverIsOpen,
}: Props) {
const [popoverIsOpen, setPopoverIsOpen] = useState(false);
const [popoverRef, setPopoverRef] = useState<HTMLElement | null>(null);
const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { fromQuery, toQuery } from '../../../../shared/Links/url_helpers';
import { formatToSec } from '../../UXMetrics/KeyUXMetrics';
import { SelectableUrlList } from './SelectableUrlList';
import { UrlOption } from './RenderOption';
import { useUxQuery } from '../../hooks/useUxQuery';

interface Props {
onChange: (value: string[]) => void;
Expand All @@ -23,9 +24,10 @@ interface Props {
export function URLSearch({ onChange: onFilterChange }: Props) {
const history = useHistory();

const { urlParams, uiFilters } = useUrlParams();
const { uiFilters } = useUrlParams();

const [popoverIsOpen, setPopoverIsOpen] = useState(false);

const { start, end, serviceName } = urlParams;
const [searchValue, setSearchValue] = useState('');

const [debouncedValue, setDebouncedValue] = useState('');
Expand Down Expand Up @@ -54,17 +56,18 @@ export function URLSearch({ onChange: onFilterChange }: Props) {

const [checkedUrls, setCheckedUrls] = useState<string[]>([]);

const uxQuery = useUxQuery();

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end && serviceName) {
if (uxQuery && popoverIsOpen) {
const { transactionUrl, ...restFilters } = uiFilters;

return callApmApi({
pathname: '/api/apm/rum-client/url-search',
params: {
query: {
start,
end,
...uxQuery,
uiFilters: JSON.stringify(restFilters),
urlQuery: searchValue,
},
Expand All @@ -73,7 +76,8 @@ export function URLSearch({ onChange: onFilterChange }: Props) {
}
return Promise.resolve(null);
},
[start, end, serviceName, uiFilters, searchValue]
// eslint-disable-next-line react-hooks/exhaustive-deps
[uxQuery, searchValue, popoverIsOpen]
);

useEffect(() => {
Expand Down Expand Up @@ -110,7 +114,9 @@ export function URLSearch({ onChange: onFilterChange }: Props) {
};

const onClose = () => {
onFilterChange(checkedUrls);
if (uiFilters.transactionUrl || checkedUrls.length > 0) {
onFilterChange(checkedUrls);
}
};

return (
Expand All @@ -126,6 +132,8 @@ export function URLSearch({ onChange: onFilterChange }: Props) {
onChange={onChange}
onClose={onClose}
searchValue={searchValue}
popoverIsOpen={popoverIsOpen}
setPopoverIsOpen={setPopoverIsOpen}
/>
</>
);
Expand Down
Loading