frontend-app-api: further routing info extraction improvements + test

Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Camila Belo <camilaibs@gmail.com>
Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-03 15:47:38 +02:00
parent 4cfc21d6eb
commit 7aad08e564
2 changed files with 152 additions and 97 deletions
@@ -24,8 +24,8 @@ import { extractRouteInfoFromInstanceTree } from './extractRouteInfoFromInstance
import {
Extension,
coreExtensionData,
createExtension,
createExtensionInput,
createPageExtension,
createPlugin,
} from '@backstage/frontend-plugin-api';
import { createInstances } from '../wiring/createApp';
@@ -38,25 +38,32 @@ const ref4 = createRouteRef({ id: 'page4' });
const ref5 = createRouteRef({ id: 'page5' });
const refOrder = [ref1, ref2, ref3, ref4, ref5];
const emptyLoader = () => Promise.resolve(React.createElement('div'));
function createTestExtension(options: {
id: string;
at?: string;
path: string;
path?: string;
routeRef?: RouteRef;
}) {
return createPageExtension({
return createExtension({
id: options.id,
at: options.at,
defaultPath: options.path,
routeRef: options.routeRef,
at: options.at ?? 'core.routes/children',
output: {
element: coreExtensionData.reactElement,
path: coreExtensionData.routePath.optional(),
routeRef: coreExtensionData.routeRef.optional(),
},
inputs: {
routes: createExtensionInput({
children: createExtensionInput({
element: coreExtensionData.reactElement,
}),
},
loader: emptyLoader,
factory({ bind }) {
bind({
path: options.path,
routeRef: options.routeRef,
element: React.createElement('div'),
});
},
});
}
@@ -119,13 +126,13 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page2',
at: 'page1/routes',
at: 'page1/children',
path: 'bar/:id',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
at: 'page2/routes',
at: 'page2/children',
path: 'baz',
routeRef: ref3,
}),
@@ -136,7 +143,7 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page5',
at: 'page1/routes',
at: 'page1/children',
path: 'blop',
routeRef: ref5,
}),
@@ -187,7 +194,7 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page2',
at: 'page1/routes',
at: 'page1/children',
path: 'bar/:id',
routeRef: ref2,
}),
@@ -198,13 +205,13 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page4',
at: 'page3/routes',
at: 'page3/children',
path: 'divsoup',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
at: 'page3/routes',
at: 'page3/children',
path: 'blop',
routeRef: ref5,
}),
@@ -227,37 +234,45 @@ describe('discovery', () => {
});
// it('should handle absolute route paths', () => {
// const root = (
// <MemoryRouter>
// <Routes>
// <Route path="/foo" element={<Extension1 />}>
// <Routes>
// <Route path="/bar/:id" element={<Extension2 />} />
// </Routes>
// </Route>
// <Route path="/baz" element={<Extension3 />}>
// <Route path="/divsoup" element={<Extension4 />} />
// <Route path="/blop" element={<Extension5 />} />
// </Route>
// </Routes>
// </MemoryRouter>
// );
// const info = routeInfoFromExtensions([
// createTestExtension({
// id: 'page1',
// path: '/foo',
// routeRef: ref1,
// }),
// createTestExtension({
// id: 'page2',
// at: 'page1/children',
// path: '/bar/:id',
// routeRef: ref2,
// }),
// createTestExtension({
// id: 'page3',
// path: '/baz',
// routeRef: ref3,
// }),
// createTestExtension({
// id: 'page4',
// at: 'page3/children',
// path: '/divsoup',
// routeRef: ref4,
// }),
// createTestExtension({
// id: 'page5',
// at: 'page3/children',
// path: '/blop',
// routeRef: ref5,
// }),
// ]);
// const { routing } = traverseElementTree({
// root,
// discoverers: [childDiscoverer, routeElementDiscoverer],
// collectors: {
// routing: routingV2Collector,
// },
// });
// expect(sortedEntries(routing.paths)).toEqual([
// expect(sortedEntries(info.routePaths)).toEqual([
// [ref1, 'foo'],
// [ref2, 'bar/:id'],
// [ref3, 'baz'],
// [ref4, 'divsoup'],
// [ref5, 'blop'],
// ]);
// expect(sortedEntries(routing.parents)).toEqual([
// expect(sortedEntries(info.routeParents)).toEqual([
// [ref1, undefined],
// [ref2, ref1],
// [ref3, undefined],
@@ -266,60 +281,76 @@ describe('discovery', () => {
// ]);
// });
// it('should use the route aggregator key to bind child routes to the same path', () => {
// const root = (
// <MemoryRouter>
// <Routes>
// <AggregationComponent path="foo">
// <Extension1 />
// <div>
// <Extension2 />
// </div>
// HELLO
// </AggregationComponent>
// <Route path="bar" element={<Extension3 />}>
// <AggregationComponent path="">
// <Extension4>
// <Extension5 />
// </Extension4>
// </AggregationComponent>
// </Route>
// </Routes>
// </MemoryRouter>
// );
it('should use the route aggregator key to bind child routes to the same path', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'foo',
path: 'foo',
}),
createTestExtension({
id: 'page1',
at: 'foo/children',
routeRef: ref1,
}),
createTestExtension({
id: 'fooChild',
at: 'foo/children',
}),
createTestExtension({
id: 'page2',
at: 'fooChild/children',
routeRef: ref2,
}),
createTestExtension({
id: 'fooEmpty',
}),
createTestExtension({
id: 'page3',
path: 'bar',
routeRef: ref3,
}),
createTestExtension({
id: 'page3Child',
at: 'page3/children',
path: '',
}),
createTestExtension({
id: 'page4',
at: 'page3Child/children',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
at: 'page4/children',
routeRef: ref5,
}),
]);
// const { routing } = traverseElementTree({
// root,
// discoverers: [childDiscoverer, routeElementDiscoverer],
// collectors: {
// routing: routingV2Collector,
// },
// });
// expect(sortedEntries(routing.paths)).toEqual([
// [ref1, 'foo'],
// [ref2, 'foo'],
// [ref3, 'bar'],
// [ref4, ''],
// [ref5, ''],
// ]);
// expect(sortedEntries(routing.parents)).toEqual([
// [ref1, undefined],
// [ref2, undefined],
// [ref3, undefined],
// [ref4, ref3],
// [ref5, ref3],
// ]);
// expect(routing.objects).toEqual([
// routeObj('foo', [ref1, ref2], [], 'gathered', plugin),
// routeObj(
// 'bar',
// [ref3],
// [routeObj('', [ref4, ref5], [], 'gathered', plugin)],
// undefined,
// plugin,
// ),
// ]);
// });
expect(sortedEntries(info.routePaths)).toEqual([
[ref1, 'foo'],
[ref2, 'foo'],
[ref3, 'bar'],
[ref4, ''],
[ref5, ''],
]);
expect(sortedEntries(info.routeParents)).toEqual([
[ref1, undefined],
[ref2, undefined],
[ref3, undefined],
[ref4, ref3],
[ref5, ref4],
]);
expect(info.routeObjects).toEqual([
routeObj('foo', [ref1, ref2], [], 'mounted', expect.any(Object)),
routeObj(
'bar',
[ref3],
[routeObj('', [ref4, ref5], [], 'mounted', expect.any(Object))],
'mounted',
expect.any(Object),
),
]);
});
// it('should use the route aggregator but stop when encountering explicit path', () => {
// const root = (
@@ -53,15 +53,19 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): {
function visit(
current: ExtensionInstance,
collectedPath?: string,
foundRefForCollectedPath: boolean = false,
parentRef?: RouteRef,
parentObj?: BackstageRouteObject,
) {
const routePath = current.getData(coreExtensionData.routePath) ?? ''; // TODO: need to gather routes instead
const routePath = current.getData(coreExtensionData.routePath);
const routeRef = current.getData(coreExtensionData.routeRef);
const parentChildren = parentObj?.children ?? routeObjects;
let currentObj = parentObj;
let newCollectedPath = collectedPath;
let newFoundRefForCollectedPath = foundRefForCollectedPath;
if (routePath) {
if (routePath !== undefined) {
currentObj = {
path: routePath,
element: 'mounted',
@@ -72,6 +76,15 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): {
};
parentChildren.push(currentObj);
if (newFoundRefForCollectedPath) {
newCollectedPath = routePath;
newFoundRefForCollectedPath = false;
} else {
newCollectedPath = collectedPath
? joinPaths(collectedPath, routePath)
: routePath;
}
}
if (routeRef) {
@@ -81,7 +94,12 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): {
`Route ref '${routeRefId}' must have the same ID as extension '${current.id}'`,
);
}
routePaths.set(routeRef, routePath);
if (newCollectedPath !== undefined) {
routePaths.set(routeRef, newCollectedPath);
newFoundRefForCollectedPath = true;
}
routeParents.set(routeRef, parentRef);
currentObj?.routeRefs.add(routeRef);
if (current.source) {
@@ -91,7 +109,13 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): {
for (const children of current.attachments.values()) {
for (const child of children) {
visit(child, routeRef ?? parentRef, currentObj);
visit(
child,
newCollectedPath,
newFoundRefForCollectedPath,
routeRef ?? parentRef,
currentObj,
);
}
}
}