Skip to content

Commit

Permalink
[7.x] [CSM] Js errors (#77919) (#78748)
Browse files Browse the repository at this point in the history
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
shahzad31 and elasticmachine authored Sep 29, 2020
1 parent 1ba7d22 commit 5516dc6
Show file tree
Hide file tree
Showing 22 changed files with 665 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,40 @@ When(/^the user filters by "([^"]*)"$/, (filterName) => {
cy.get('.euiStat__title-isLoading').should('not.be.visible');
cy.get(`#local-filter-${filterName}`).click();

if (filterName === 'os') {
cy.get('span.euiSelectableListItem__text', DEFAULT_TIMEOUT)
.contains('Mac OS X')
.click();
} else {
cy.get('span.euiSelectableListItem__text', DEFAULT_TIMEOUT)
.contains('DE')
.click();
}
cy.get('[data-cy=applyFilter]').click();
cy.get(`#local-filter-popover-${filterName}`, DEFAULT_TIMEOUT).within(() => {
if (filterName === 'os') {
const osItem = cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(2);
osItem.should('have.text', 'Mac OS X8 ');
osItem.click();

// sometimes click doesn't work as expected so we need to retry here
osItem.invoke('attr', 'aria-selected').then((val) => {
if (val === 'false') {
cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(2).click();
}
});
} else {
const deItem = cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(0);
deItem.should('have.text', 'DE28 ');
deItem.click();

// sometimes click doesn't work as expected so we need to retry here
deItem.invoke('attr', 'aria-selected').then((val) => {
if (val === 'false') {
cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(0).click();
}
});
}
cy.get('[data-cy=applyFilter]').click();
});

cy.get(`div#local-filter-values-${filterName}`, DEFAULT_TIMEOUT).within(
() => {
cy.get('span.euiBadge__content')
.eq(0)
.should('have.text', filterName === 'os' ? 'Mac OS X' : 'DE');
}
);
});

Then(/^it filters the client metrics "([^"]*)"$/, (filterName) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
*/

import { When, Then } from 'cypress-cucumber-preprocessor/steps';
import { DEFAULT_TIMEOUT } from '../apm';
import { verifyClientMetrics } from './client_metrics_helper';
import { DEFAULT_TIMEOUT } from './csm_dashboard';

When('the user changes the selected service name', (filterName) => {
When('the user changes the selected service name', () => {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get(`[data-cy=serviceNameFilter]`, { timeout: DEFAULT_TIMEOUT }).select(
'client'
);
cy.get(`[data-cy=serviceNameFilter]`, DEFAULT_TIMEOUT).select('client');
});

Then(`it displays relevant client metrics`, () => {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/apm/e2e/ingest-data/replay.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function setRumAgent(item) {
if (item.body) {
item.body = item.body.replace(
'"name":"client"',
'"name":"opbean-client-rum"'
'"name":"elastic-frontend"'
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import * as React from 'react';
import numeral from '@elastic/numeral';
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 { CsmSharedContext } from '../CsmSharedContext';

const ClFlexGroup = styled(EuiFlexGroup)`
flex-direction: row;
Expand Down Expand Up @@ -45,6 +47,12 @@ export function ClientMetrics() {
[start, end, uiFilters, searchTerm]
);

const { setSharedData } = useContext(CsmSharedContext);

useEffect(() => {
setSharedData({ totalPageViews: data?.pageViews?.value ?? 0 });
}, [data, setSharedData]);

const STAT_STYLE = { width: '240px' };

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 React, { createContext, useMemo, useState } from 'react';

interface SharedData {
totalPageViews: number;
}

interface Index {
sharedData: SharedData;
setSharedData: (data: SharedData) => void;
}

const defaultContext: Index = {
sharedData: { totalPageViews: 0 },
setSharedData: (d) => {
throw new Error(
'setSharedData was not initialized, set it when you invoke the context'
);
},
};

export const CsmSharedContext = createContext(defaultContext);

export function CsmSharedContextProvider({
children,
}: {
children: JSX.Element[];
}) {
const [newData, setNewData] = useState<SharedData>({ totalPageViews: 0 });

const setSharedData = React.useCallback((data: SharedData) => {
setNewData(data);
}, []);

const value = useMemo(() => {
return { sharedData: newData, setSharedData };
}, [newData, setSharedData]);

return <CsmSharedContext.Provider value={value} children={children} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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 React, { useContext, useState } from 'react';
import {
EuiBasicTable,
EuiFlexItem,
EuiFlexGroup,
EuiSpacer,
EuiTitle,
EuiStat,
EuiToolTip,
} from '@elastic/eui';
import numeral from '@elastic/numeral';
import { i18n } from '@kbn/i18n';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { useFetcher } from '../../../../hooks/useFetcher';
import { I18LABELS } from '../translations';
import { CsmSharedContext } from '../CsmSharedContext';

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

const { start, end, serviceName } = urlParams;

const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 5 });

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/js-errors',
params: {
query: {
start,
end,
uiFilters: JSON.stringify(uiFilters),
pageSize: String(pagination.pageSize),
pageIndex: String(pagination.pageIndex),
},
},
});
}
return Promise.resolve(null);
},
[start, end, serviceName, uiFilters, pagination]
);

const {
sharedData: { totalPageViews },
} = useContext(CsmSharedContext);

const items = (data?.items ?? []).map(({ errorMessage, count }) => ({
errorMessage,
percent: i18n.translate('xpack.apm.rum.jsErrors.percent', {
defaultMessage: '{pageLoadPercent} %',
values: { pageLoadPercent: ((count / totalPageViews) * 100).toFixed(1) },
}),
}));

const cols = [
{
field: 'errorMessage',
name: I18LABELS.errorMessage,
},
{
name: I18LABELS.impactedPageLoads,
field: 'percent',
align: 'right' as const,
},
];

const onTableChange = ({
page,
}: {
page: { size: number; index: number };
}) => {
setPagination({
pageIndex: page.index,
pageSize: page.size,
});
};

return (
<>
<EuiTitle size="xs">
<h3>{I18LABELS.jsErrors}</h3>
</EuiTitle>
<EuiSpacer size="s" />
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiStat
titleSize="s"
title={
<EuiToolTip content={data?.totalErrors ?? 0}>
<>{numeral(data?.totalErrors ?? 0).format('0 a')}</>
</EuiToolTip>
}
description={I18LABELS.totalErrors}
isLoading={status !== 'success'}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiStat
titleSize="s"
title={i18n.translate('xpack.apm.rum.jsErrors.errorRateValue', {
defaultMessage: '{errorRate} %',
values: {
errorRate: (
((data?.totalErrorPages ?? 0) / totalPageViews) *
100
).toFixed(0),
},
})}
description={I18LABELS.errorRate}
isLoading={status !== 'success'}
/>
</EuiFlexItem>{' '}
</EuiFlexGroup>
<EuiSpacer size="s" />
<EuiBasicTable
loading={status !== 'success'}
responsive={false}
compressed={true}
columns={cols}
items={items}
onChange={onTableChange}
pagination={{
...pagination,
totalItemCount: data?.totalErrorGroups ?? 0,
}}
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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 React from 'react';
import { EuiFlexItem, EuiPanel, EuiFlexGroup, EuiSpacer } from '@elastic/eui';
import { JSErrors } from './JSErrors';

export function ImpactfulMetrics() {
return (
<EuiPanel>
<EuiSpacer size="xs" />
<EuiFlexGroup wrap>
<EuiFlexItem style={{ flexBasis: 650 }}>
<JSErrors />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { I18LABELS } from './translations';
import { VisitorBreakdown } from './VisitorBreakdown';
import { UXMetrics } from './UXMetrics';
import { VisitorBreakdownMap } from './VisitorBreakdownMap';
import { ImpactfulMetrics } from './ImpactfulMetrics';

export function RumDashboard() {
return (
Expand Down Expand Up @@ -66,6 +67,9 @@ export function RumDashboard() {
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<ImpactfulMetrics />
</EuiFlexItem>
</EuiFlexGroup>
);
}
23 changes: 13 additions & 10 deletions x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx
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 { CsmSharedContextProvider } from './CsmSharedContext';

export const UX_LABEL = i18n.translate('xpack.apm.ux.title', {
defaultMessage: 'User Experience',
Expand All @@ -17,16 +18,18 @@ export const UX_LABEL = i18n.translate('xpack.apm.ux.title', {
export function RumHome() {
return (
<div>
<RumHeader>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<h1>{UX_LABEL}</h1>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
</RumHeader>
<RumOverview />
<CsmSharedContextProvider>
<RumHeader>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<h1>{UX_LABEL}</h1>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
</RumHeader>
<RumOverview />
</CsmSharedContextProvider>
</div>
);
}
Loading

0 comments on commit 5516dc6

Please sign in to comment.