[APM] Support multiple route paths in useApmParams (#109370)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Dario Gieselaar 2021-08-23 15:38:06 +02:00 committed by GitHub
parent 60a74e8eaf
commit 6e3af2b524
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 21 deletions

View file

@ -201,6 +201,21 @@ describe('createRouter', () => {
},
});
});
it('supports multiple paths', () => {
history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3');
const params = router.getParams('/services', '/service-map', history.location);
expect(params).toEqual({
path: {},
query: {
maxNumNodes: 3,
rangeFrom: 'now-15m',
rangeTo: 'now',
},
});
});
});
describe('matchRoutes', () => {

View file

@ -9,6 +9,7 @@ import { isLeft } from 'fp-ts/lib/Either';
import { Location } from 'history';
import { PathReporter } from 'io-ts/lib/PathReporter';
import {
MatchedRoute,
matchRoutes as matchRoutesConfig,
RouteConfig as ReactRouterConfig,
} from 'react-router-config';
@ -49,33 +50,44 @@ export function createRouter<TRoutes extends Route[]>(routes: TRoutes): Router<T
}
const matchRoutes = (...args: any[]) => {
let path: string = args[0];
let location: Location = args[1];
let optional: boolean = args[2];
let optional: boolean = false;
if (args.length === 1) {
location = args[0] as Location;
path = location.pathname;
optional = args[1];
if (typeof args[args.length - 1] === 'boolean') {
optional = args[args.length - 1];
args.pop();
}
const greedy = path.endsWith('/*') || args.length === 1;
const location: Location = args[args.length - 1];
args.pop();
if (!path) {
path = '/';
let paths: string[] = args;
if (paths.length === 0) {
paths = [location.pathname || '/'];
}
const matches = matchRoutesConfig(reactRouterConfigs, location.pathname);
let matches: Array<MatchedRoute<{}, ReactRouterConfig>> = [];
let matchIndex: number = -1;
const matchIndex = greedy
? matches.length - 1
: findLastIndex(matches, (match) => match.route.path === path);
for (const path of paths) {
const greedy = path.endsWith('/*') || args.length === 0;
matches = matchRoutesConfig(reactRouterConfigs, location.pathname);
matchIndex = greedy
? matches.length - 1
: findLastIndex(matches, (match) => match.route.path === path);
if (matchIndex !== -1) {
break;
}
matchIndex = -1;
}
if (matchIndex === -1) {
if (optional) {
return [];
}
throw new Error(`No matching route found for ${path}`);
throw new Error(`No matching route found for ${paths}`);
}
return matches.slice(0, matchIndex + 1).map((matchedRoute) => {

View file

@ -134,6 +134,22 @@ export interface Router<TRoutes extends Route[]> {
location: Location,
optional: TOptional
): TOptional extends true ? TypeOf<TRoutes, TPath> | undefined : TypeOf<TRoutes, TPath>;
getParams<T1 extends PathsOf<TRoutes>, T2 extends PathsOf<TRoutes>>(
path1: T1,
path2: T2,
location: Location
): TypeOf<TRoutes, T1> | TypeOf<TRoutes, T2>;
getParams<T1 extends PathsOf<TRoutes>, T2 extends PathsOf<TRoutes>, T3 extends PathsOf<TRoutes>>(
path1: T1,
path2: T2,
path3: T3,
location: Location
): TypeOf<TRoutes, T1> | TypeOf<TRoutes, T2> | TypeOf<TRoutes, T3>;
getParams<TPath extends PathsOf<TRoutes>, TOptional extends boolean>(
path: TPath,
location: Location,
optional: TOptional
): TOptional extends true ? TypeOf<TRoutes, TPath> | undefined : TypeOf<TRoutes, TPath>;
link<TPath extends PathsOf<TRoutes>>(
path: TPath,
...args: TypeAsArgs<TypeOf<TRoutes, TPath, false>>

View file

@ -6,12 +6,26 @@
* Side Public License, v 1.
*/
import { Location } from 'history';
import { useLocation } from 'react-router-dom';
import { useRouter } from './use_router';
export function useParams(path: string, optional: boolean = false) {
export function useParams(...args: any[]) {
const router = useRouter();
const location = useLocation();
return router.getParams(path as never, location, optional);
let optional: boolean = false;
const last: boolean | string | undefined = args[args.length - 1];
if (typeof last === 'boolean') {
optional = last;
args.pop();
}
const paths = args as string[];
const getParamsArgs = [...paths, location, optional] as [never, Location<any>, boolean];
return router.getParams(...getParamsArgs);
}

View file

@ -21,7 +21,11 @@ import { ApmRoutes } from '../../../routing/apm_route_config';
import { StatsList } from './stats_list';
export function BackendContents({ nodeData, environment }: ContentsProps) {
const { query } = useApmParams('/*');
const { query } = useApmParams(
'/service-map',
'/services/:serviceName/service-map'
);
const apmRouter = useApmRouter();
const {
urlParams: { start, end },

View file

@ -17,9 +17,29 @@ export function useApmParams<TPath extends PathsOf<ApmRoutes>>(
path: TPath
): TypeOf<ApmRoutes, TPath>;
export function useApmParams<
TPath1 extends PathsOf<ApmRoutes>,
TPath2 extends PathsOf<ApmRoutes>
>(
path1: TPath1,
path2: TPath2
): TypeOf<ApmRoutes, TPath1> | TypeOf<ApmRoutes, TPath2>;
export function useApmParams<
TPath1 extends PathsOf<ApmRoutes>,
TPath2 extends PathsOf<ApmRoutes>,
TPath3 extends PathsOf<ApmRoutes>
>(
path1: TPath1,
path2: TPath2,
path3: TPath3
):
| TypeOf<ApmRoutes, TPath1>
| TypeOf<ApmRoutes, TPath2>
| TypeOf<ApmRoutes, TPath3>;
export function useApmParams(
path: string,
optional?: true
...args: any[]
): TypeOf<ApmRoutes, PathsOf<ApmRoutes>> | undefined {
return useParams(path, optional);
return useParams(...args);
}

View file

@ -10,6 +10,7 @@ import '../../../typings/rison_node';
import '../../infra/types/eui';
// EUIBasicTable
import '../../reporting/public/components/report_listing';
import '../../reporting/server/lib/puid';
import './apm_rum_react';
// Allow unknown properties in an object