Skip to content

Commit 66263d6

Browse files
authored
[7.x] [APM] Context-aware query examples for the query bar (#3… (elastic#39570)
* [APM] Context-aware query examples for the query bar We now adjust the query example based on whether the user is viewing transactions, errors or metrics. * Change query example for transactions * Address review feedback * Fix ts issues in unit tests * Use enum for route names, clarify queryExample w/ comment
1 parent 175d576 commit 66263d6

File tree

10 files changed

+143
-39
lines changed

10 files changed

+143
-39
lines changed

x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
withRouter
1414
} from 'react-router-dom';
1515
import { StringMap } from '../../../../typings/common';
16+
import { RouteName } from './route_config/route_names';
1617

1718
type LocationMatch = Pick<
1819
RouteComponentProps<StringMap<string>>,
@@ -23,6 +24,7 @@ type BreadcrumbFunction = (props: LocationMatch) => string;
2324

2425
export interface BreadcrumbRoute extends RouteProps {
2526
breadcrumb: string | BreadcrumbFunction | null;
27+
name: RouteName;
2628
}
2729

2830
export interface Breadcrumb extends LocationMatch {

x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React from 'react';
1010
import chrome from 'ui/chrome';
1111
import { getAPMHref } from '../../shared/Links/APMLink';
1212
import { Breadcrumb, ProvideBreadcrumbs } from './ProvideBreadcrumbs';
13-
import { routes } from './routeConfig';
13+
import { routes } from './route_config';
1414

1515
interface Props {
1616
location: Location;

x-pack/legacy/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,27 @@
66

77
import { Location } from 'history';
88
import { BreadcrumbRoute, getBreadcrumbs } from '../ProvideBreadcrumbs';
9+
import { RouteName } from '../route_config/route_names';
910

1011
describe('getBreadcrumbs', () => {
1112
const getTestRoutes = (): BreadcrumbRoute[] => [
12-
{ path: '/a', exact: true, breadcrumb: 'A' },
13-
{ path: '/a/ignored', exact: true, breadcrumb: 'Ignored Route' },
13+
{ path: '/a', exact: true, breadcrumb: 'A', name: RouteName.HOME },
14+
{
15+
path: '/a/ignored',
16+
exact: true,
17+
breadcrumb: 'Ignored Route',
18+
name: RouteName.METRICS
19+
},
1420
{
1521
path: '/a/:letter',
1622
exact: true,
23+
name: RouteName.SERVICE,
1724
breadcrumb: ({ match }) => `Second level: ${match.params.letter}`
1825
},
1926
{
2027
path: '/a/:letter/c',
2128
exact: true,
29+
name: RouteName.ERRORS,
2230
breadcrumb: ({ match }) => `Third level: ${match.params.letter}`
2331
}
2432
];

x-pack/legacy/plugins/apm/public/components/app/Main/routeConfig.tsx renamed to x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
import { i18n } from '@kbn/i18n';
88
import React from 'react';
99
import { Redirect, RouteComponentProps } from 'react-router-dom';
10-
import { legacyDecodeURIComponent } from '../../shared/Links/url_helpers';
11-
import { ErrorGroupDetails } from '../ErrorGroupDetails';
12-
import { ServiceDetails } from '../ServiceDetails';
13-
import { TransactionDetails } from '../TransactionDetails';
14-
import { Home } from './Home';
15-
import { BreadcrumbRoute } from './ProvideBreadcrumbs';
10+
import { legacyDecodeURIComponent } from '../../../shared/Links/url_helpers';
11+
import { ErrorGroupDetails } from '../../ErrorGroupDetails';
12+
import { ServiceDetails } from '../../ServiceDetails';
13+
import { TransactionDetails } from '../../TransactionDetails';
14+
import { Home } from '../Home';
15+
import { BreadcrumbRoute } from '../ProvideBreadcrumbs';
16+
import { RouteName } from './route_names';
1617

1718
interface RouteParams {
1819
serviceName: string;
@@ -34,23 +35,26 @@ export const routes: BreadcrumbRoute[] = [
3435
exact: true,
3536
path: '/',
3637
render: renderAsRedirectTo('/services'),
37-
breadcrumb: 'APM'
38+
breadcrumb: 'APM',
39+
name: RouteName.HOME
3840
},
3941
{
4042
exact: true,
4143
path: '/services',
4244
component: Home,
4345
breadcrumb: i18n.translate('xpack.apm.breadcrumb.servicesTitle', {
4446
defaultMessage: 'Services'
45-
})
47+
}),
48+
name: RouteName.SERVICES
4649
},
4750
{
4851
exact: true,
4952
path: '/traces',
5053
component: Home,
5154
breadcrumb: i18n.translate('xpack.apm.breadcrumb.tracesTitle', {
5255
defaultMessage: 'Traces'
53-
})
56+
}),
57+
name: RouteName.TRACES
5458
},
5559
{
5660
exact: true,
@@ -59,51 +63,58 @@ export const routes: BreadcrumbRoute[] = [
5963
render: (props: RouteComponentProps<RouteParams>) =>
6064
renderAsRedirectTo(`/${props.match.params.serviceName}/transactions`)(
6165
props
62-
)
66+
),
67+
name: RouteName.SERVICE
6368
},
6469
{
6570
exact: true,
6671
path: '/:serviceName/errors/:groupId',
6772
component: ErrorGroupDetails,
68-
breadcrumb: ({ match }) => match.params.groupId
73+
breadcrumb: ({ match }) => match.params.groupId,
74+
name: RouteName.ERROR
6975
},
7076
{
7177
exact: true,
7278
path: '/:serviceName/errors',
7379
component: ServiceDetails,
7480
breadcrumb: i18n.translate('xpack.apm.breadcrumb.errorsTitle', {
7581
defaultMessage: 'Errors'
76-
})
82+
}),
83+
name: RouteName.ERRORS
7784
},
7885
{
7986
exact: true,
8087
path: '/:serviceName/transactions',
8188
component: ServiceDetails,
8289
breadcrumb: i18n.translate('xpack.apm.breadcrumb.transactionsTitle', {
8390
defaultMessage: 'Transactions'
84-
})
91+
}),
92+
name: RouteName.TRANSACTIONS
8593
},
8694
// Have to split this out as its own route to prevent duplicate breadcrumbs from both matching
8795
// if we use :transactionType? as a single route here
8896
{
8997
exact: true,
9098
path: '/:serviceName/transactions/:transactionType',
9199
component: ServiceDetails,
92-
breadcrumb: null
100+
breadcrumb: null,
101+
name: RouteName.TRANSACTION_TYPE
93102
},
94103
{
95104
exact: true,
96105
path: '/:serviceName/metrics',
97106
component: ServiceDetails,
98107
breadcrumb: i18n.translate('xpack.apm.breadcrumb.metricsTitle', {
99108
defaultMessage: 'Metrics'
100-
})
109+
}),
110+
name: RouteName.METRICS
101111
},
102112
{
103113
exact: true,
104114
path: '/:serviceName/transactions/:transactionType/:transactionName',
105115
component: TransactionDetails,
106116
breadcrumb: ({ match }) =>
107-
legacyDecodeURIComponent(match.params.transactionName) || ''
117+
legacyDecodeURIComponent(match.params.transactionName) || '',
118+
name: RouteName.TRANSACTION_NAME
108119
}
109120
];
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export enum RouteName {
8+
HOME = 'home',
9+
SERVICES = 'services',
10+
TRACES = 'traces',
11+
SERVICE = 'service',
12+
TRANSACTIONS = 'transactions',
13+
ERRORS = 'errors',
14+
ERROR = 'error',
15+
METRICS = 'metrics',
16+
TRANSACTION_TYPE = 'transaction_type',
17+
TRANSACTION_NAME = 'transaction_name'
18+
}

x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ export class Typeahead extends Component {
156156
};
157157

158158
render() {
159+
const { queryExample } = this.props;
160+
159161
return (
160162
<ClickOutside
161163
onClickOutside={this.onClickOutside}
@@ -171,10 +173,9 @@ export class Typeahead extends Component {
171173
'xpack.apm.kueryBar.searchPlaceholder',
172174
{
173175
defaultMessage:
174-
'Search transactions and errors… (E.g. {queryExample})',
176+
'Search transactions, errors and metrics… (E.g. {queryExample})',
175177
values: {
176-
queryExample:
177-
'transaction.duration.us > 300000 AND http.response.status_code >= 400'
178+
queryExample
178179
}
179180
}
180181
)}
@@ -224,7 +225,8 @@ Typeahead.propTypes = {
224225
disabled: PropTypes.bool,
225226
onChange: PropTypes.func.isRequired,
226227
onSubmit: PropTypes.func.isRequired,
227-
suggestions: PropTypes.array.isRequired
228+
suggestions: PropTypes.array.isRequired,
229+
queryExample: PropTypes.string.isRequired
228230
};
229231

230232
Typeahead.defaultProps = {

x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import { getBoolFilter } from './get_bool_filter';
2727
import { useLocation } from '../../../hooks/useLocation';
2828
import { useUrlParams } from '../../../hooks/useUrlParams';
2929
import { history } from '../../../utils/history';
30+
import { useMatchedRoutes } from '../../../hooks/useMatchedRoutes';
31+
import { RouteName } from '../../app/Main/route_config/route_names';
3032

3133
const Container = styled.div`
3234
margin-bottom: 10px;
@@ -48,11 +50,24 @@ export function KueryBar() {
4850
});
4951
const { urlParams } = useUrlParams();
5052
const location = useLocation();
53+
const matchedRoutes = useMatchedRoutes();
54+
5155
const apmIndexPatternTitle = chrome.getInjected('apmIndexPatternTitle');
5256
const indexPatternMissing =
5357
!state.isLoadingIndexPattern && !state.indexPattern;
5458
let currentRequestCheck;
5559

60+
const exampleMap: { [key: string]: string } = {
61+
[RouteName.TRANSACTIONS]: 'transaction.duration.us > 300000',
62+
[RouteName.ERRORS]: 'http.response.status_code >= 400',
63+
[RouteName.METRICS]: 'process.pid = "1234"'
64+
};
65+
66+
// sets queryExample to the first matched example query, else default example
67+
const queryExample =
68+
matchedRoutes.map(({ name }) => exampleMap[name]).find(Boolean) ||
69+
'transaction.duration.us > 300000 AND http.response.status_code >= 400';
70+
5671
useEffect(() => {
5772
let didCancel = false;
5873

@@ -143,6 +158,7 @@ export function KueryBar() {
143158
onChange={onChange}
144159
onSubmit={onSubmit}
145160
suggestions={state.suggestions}
161+
queryExample={queryExample}
146162
/>
147163

148164
{indexPatternMissing && (
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React, { useMemo } from 'react';
7+
import { matchPath } from 'react-router-dom';
8+
import { routes } from '../components/app/Main/route_config';
9+
import { useLocation } from '../hooks/useLocation';
10+
11+
export const MatchedRouteContext = React.createContext<Array<typeof routes[0]>>(
12+
[]
13+
);
14+
15+
export const MatchedRouteProvider: React.FC = ({ children }) => {
16+
const { pathname } = useLocation();
17+
18+
const contextValue = useMemo(
19+
() => {
20+
return routes.filter(route => {
21+
return matchPath(pathname, {
22+
path: route.path
23+
});
24+
});
25+
},
26+
[pathname]
27+
);
28+
29+
return (
30+
<MatchedRouteContext.Provider value={contextValue} children={children} />
31+
);
32+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { useContext } from 'react';
8+
import { MatchedRouteContext } from '../context/MatchedRouteContext';
9+
10+
export function useMatchedRoutes() {
11+
return useContext(MatchedRouteContext);
12+
}

x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import { px, topNavHeight, unit, units } from '../style/variables';
1616
import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext';
1717
import { LicenseProvider } from '../context/LicenseContext';
1818
import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs';
19-
import { routes } from '../components/app/Main/routeConfig';
19+
import { routes } from '../components/app/Main/route_config';
2020
import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange';
2121
import { useUpdateBadgeEffect } from '../components/app/Main/useUpdateBadgeEffect';
22+
import { MatchedRouteProvider } from '../context/MatchedRouteContext';
2223

2324
export const REACT_APP_ROOT_ID = 'react-apm-root';
2425

@@ -32,21 +33,23 @@ function App() {
3233
useUpdateBadgeEffect();
3334

3435
return (
35-
<UrlParamsProvider>
36-
<LoadingIndicatorProvider>
37-
<MainContainer data-test-subj="apmMainContainer">
38-
<UpdateBreadcrumbs />
39-
<Route component={ScrollToTopOnPathChange} />
40-
<LicenseProvider>
41-
<Switch>
42-
{routes.map((route, i) => (
43-
<Route key={i} {...route} />
44-
))}
45-
</Switch>
46-
</LicenseProvider>
47-
</MainContainer>
48-
</LoadingIndicatorProvider>
49-
</UrlParamsProvider>
36+
<MatchedRouteProvider>
37+
<UrlParamsProvider>
38+
<LoadingIndicatorProvider>
39+
<MainContainer data-test-subj="apmMainContainer">
40+
<UpdateBreadcrumbs />
41+
<Route component={ScrollToTopOnPathChange} />
42+
<LicenseProvider>
43+
<Switch>
44+
{routes.map((route, i) => (
45+
<Route key={i} {...route} />
46+
))}
47+
</Switch>
48+
</LicenseProvider>
49+
</MainContainer>
50+
</LoadingIndicatorProvider>
51+
</UrlParamsProvider>
52+
</MatchedRouteProvider>
5053
);
5154
}
5255

0 commit comments

Comments
 (0)