Merge pull request #3818 from backstage/mob/gratherobj

core-api: gather all relevant route refs into each route object
This commit is contained in:
Patrik Oldsberg
2020-12-22 13:09:15 +01:00
committed by GitHub
4 changed files with 84 additions and 18 deletions
@@ -15,7 +15,11 @@
*/
import React, { PropsWithChildren } from 'react';
import { routePathCollector, routeParentCollector } from './collectors';
import {
routePathCollector,
routeParentCollector,
routeObjectCollector,
} from './collectors';
import {
traverseElementTree,
@@ -86,6 +90,16 @@ function sortedEntries<T>(map: Map<RouteRef, T>): [RouteRef, T][] {
);
}
function routeObj(path: string, refs: RouteRef[], children: any[] = []) {
return {
path: path,
caseSensitive: false,
element: null,
routeRefs: new Set(refs),
children: children,
};
}
describe('discovery', () => {
it('should collect routes', () => {
const list = [
@@ -124,12 +138,13 @@ describe('discovery', () => {
</MemoryRouter>
);
const { routes, routeParents } = traverseElementTree({
const { routes, routeParents, routeObjects } = traverseElementTree({
root,
discoverers: [childDiscoverer, routeElementDiscoverer],
collectors: {
routes: routePathCollector,
routeParents: routeParentCollector,
routeObjects: routeObjectCollector,
},
});
expect(sortedEntries(routes)).toEqual([
@@ -146,6 +161,17 @@ describe('discovery', () => {
[ref4, undefined],
[ref5, ref1],
]);
expect(routeObjects).toEqual([
routeObj(
'/foo',
[ref1],
[
routeObj('/bar/:id', [ref2], [routeObj('/baz', [ref3])]),
routeObj('/blop', [ref5]),
],
),
routeObj('/divsoup', [ref4]),
]);
});
it('should handle all react router Route patterns', () => {
@@ -216,12 +242,13 @@ describe('discovery', () => {
</MemoryRouter>
);
const { routes, routeParents } = traverseElementTree({
const { routes, routeParents, routeObjects } = traverseElementTree({
root,
discoverers: [childDiscoverer, routeElementDiscoverer],
collectors: {
routes: routePathCollector,
routeParents: routeParentCollector,
routeObjects: routeObjectCollector,
},
});
expect(sortedEntries(routes)).toEqual([
@@ -238,6 +265,10 @@ describe('discovery', () => {
[ref4, ref3],
[ref5, ref3],
]);
expect(routeObjects).toEqual([
routeObj('/foo', [ref1, ref2]),
routeObj('/bar', [ref3], [routeObj('/baz', [ref4, ref5])]),
]);
});
it('should use the route aggregator but stop when encountering explicit path', () => {
@@ -258,12 +289,13 @@ describe('discovery', () => {
</MemoryRouter>
);
const { routes, routeParents } = traverseElementTree({
const { routes, routeParents, routeObjects } = traverseElementTree({
root,
discoverers: [childDiscoverer, routeElementDiscoverer],
collectors: {
routes: routePathCollector,
routeParents: routeParentCollector,
routeObjects: routeObjectCollector,
},
});
expect(sortedEntries(routes)).toEqual([
@@ -280,6 +312,19 @@ describe('discovery', () => {
[ref4, ref3],
[ref5, ref1],
]);
expect(routeObjects).toEqual([
routeObj(
'/foo',
[ref1],
[
routeObj(
'/bar',
[ref2, ref5],
[routeObj('/baz', [ref3], [routeObj('/blop', [ref4])])],
),
],
),
]);
});
it('should stop gathering mount points after encountering explicit path', () => {
+32 -11
View File
@@ -111,9 +111,10 @@ export const routeParentCollector = createCollector(
export const routeObjectCollector = createCollector(
() => Array<BackstageRouteObject>(),
(acc, node, parent, parentChildArr: BackstageRouteObject[] = acc) => {
(acc, node, parent, parentObj: BackstageRouteObject | undefined) => {
const parentChildren = parentObj?.children ?? acc;
if (parent?.props.element === node) {
return parentChildArr;
return parentObj;
}
const path: string | undefined = node.props?.path;
@@ -121,20 +122,40 @@ export const routeObjectCollector = createCollector(
const routeRef = getMountPoint(node);
if (routeRef) {
const children: BackstageRouteObject[] = [];
if (!path) {
throw new Error(`No path found for mount point ${routeRef}`);
if (path) {
const newObject: BackstageRouteObject = {
caseSensitive,
path,
element: null,
routeRefs: new Set([routeRef]),
children: [],
};
parentChildren.push(newObject);
return newObject;
}
parentChildArr.push({
parentObj?.routeRefs.add(routeRef);
}
const isGatherer = getComponentData<boolean>(
node,
'core.gatherMountPoints',
);
if (isGatherer) {
if (!path) {
throw new Error('Mount point gatherer must have a path');
}
const newObject: BackstageRouteObject = {
caseSensitive,
path,
element: null,
routeRef,
children,
});
return children;
routeRefs: new Set(),
children: [],
};
parentChildren.push(newObject);
return newObject;
}
return parentChildArr;
return parentObj;
},
);
+2 -2
View File
@@ -60,8 +60,8 @@ class RouteResolver {
currentRouteRef;
currentRouteRef = this.routeParents.get(currentRouteRef)
) {
matchIndex = match.findIndex(
m => (m.route as BackstageRouteObject).routeRef === currentRouteRef,
matchIndex = match.findIndex(m =>
(m.route as BackstageRouteObject).routeRefs.has(currentRouteRef!),
);
if (matchIndex !== -1) {
break;
+1 -1
View File
@@ -62,5 +62,5 @@ export interface BackstageRouteObject {
children?: BackstageRouteObject[];
element: React.ReactNode;
path: string;
routeRef: AnyRouteRef;
routeRefs: Set<AnyRouteRef>;
}