core-plugin-api: add forwards compatibility for route refs
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -22,7 +22,12 @@ import {
|
||||
RouteRef,
|
||||
SubRouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
BackstagePlugin,
|
||||
createRouteRef as createLegacyRouteRef,
|
||||
createSubRouteRef as createLegacySubRouteRef,
|
||||
createExternalRouteRef as createLegacyExternalRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { RouteResolver } from './RouteResolver';
|
||||
import { MATCH_ALL_ROUTE } from './extractRouteInfoFromAppNode';
|
||||
import {
|
||||
@@ -416,4 +421,196 @@ describe('RouteResolver', () => {
|
||||
'/my-parent/a%2F%23%26%3Fb',
|
||||
);
|
||||
});
|
||||
|
||||
describe('with legacy route refs', () => {
|
||||
const legacyRef1 = createLegacyRouteRef({ id: 'ref1' });
|
||||
const legacyRef2 = createLegacyRouteRef({ id: 'ref2', params: ['x'] });
|
||||
const legacyRef3 = createLegacyRouteRef({ id: 'ref3', params: ['y'] });
|
||||
const legacySubRef1 = createLegacySubRouteRef({
|
||||
id: 'sub1',
|
||||
parent: legacyRef1,
|
||||
path: '/foo',
|
||||
});
|
||||
const legacySubRef2 = createLegacySubRouteRef({
|
||||
id: 'sub2',
|
||||
parent: legacyRef1,
|
||||
path: '/foo/:a',
|
||||
});
|
||||
const legacySubRef3 = createLegacySubRouteRef({
|
||||
id: 'sub3',
|
||||
parent: legacyRef2,
|
||||
path: '/bar',
|
||||
});
|
||||
const legacySubRef4 = createLegacySubRouteRef({
|
||||
id: 'sub4',
|
||||
parent: legacyRef2,
|
||||
path: '/bar/:a',
|
||||
});
|
||||
const legacyExternalRef1 = createLegacyExternalRouteRef({
|
||||
id: 'external1',
|
||||
});
|
||||
const legacyExternalRef2 = createLegacyExternalRouteRef({
|
||||
id: 'external2',
|
||||
params: ['x'],
|
||||
});
|
||||
|
||||
it('should not resolve anything with an empty resolver', () => {
|
||||
const r = new RouteResolver(
|
||||
new Map(),
|
||||
new Map(),
|
||||
[],
|
||||
new Map(),
|
||||
'',
|
||||
emptyResolver,
|
||||
new Map(),
|
||||
);
|
||||
|
||||
expect(r.resolve(legacyRef1, src('/'))?.()).toBe(undefined);
|
||||
expect(r.resolve(legacyRef2, src('/'))?.({ x: '1x' })).toBe(undefined);
|
||||
expect(r.resolve(legacySubRef1, src('/'))?.()).toBe(undefined);
|
||||
expect(r.resolve(legacySubRef2, src('/'))?.({ a: '2a' })).toBe(undefined);
|
||||
expect(r.resolve(legacySubRef3, src('/'))?.({ x: '3x' })).toBe(undefined);
|
||||
expect(r.resolve(legacySubRef4, src('/'))?.({ x: '4x', a: '4a' })).toBe(
|
||||
undefined,
|
||||
);
|
||||
expect(r.resolve(legacyExternalRef1, src('/'))?.()).toBe(undefined);
|
||||
expect(r.resolve(legacyExternalRef2, src('/'))?.({ x: '5x' })).toBe(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve an absolute route', () => {
|
||||
const r = new RouteResolver(
|
||||
new Map([[legacyRef1, 'my-route']]),
|
||||
new Map(),
|
||||
[{ routeRefs: new Set([legacyRef1]), path: 'my-route', ...rest }],
|
||||
new Map(),
|
||||
'',
|
||||
emptyResolver,
|
||||
new Map(),
|
||||
);
|
||||
|
||||
expect(r.resolve(legacyRef1, src('/'))?.()).toBe('/my-route');
|
||||
expect(r.resolve(legacyRef2, src('/'))?.({ x: '1x' })).toBe(undefined);
|
||||
expect(r.resolve(legacySubRef1, src('/'))?.()).toBe('/my-route/foo');
|
||||
expect(r.resolve(legacySubRef2, src('/'))?.({ a: '2a' })).toBe(
|
||||
'/my-route/foo/2a',
|
||||
);
|
||||
expect(r.resolve(legacySubRef3, src('/'))?.({ x: '3x' })).toBe(undefined);
|
||||
expect(r.resolve(legacySubRef4, src('/'))?.({ x: '4x', a: '4a' })).toBe(
|
||||
undefined,
|
||||
);
|
||||
expect(r.resolve(legacyExternalRef1, src('/'))?.()).toBe(undefined);
|
||||
expect(r.resolve(legacyExternalRef2, src('/'))?.({ x: '5x' })).toBe(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve an absolute route with a param and with a parent', () => {
|
||||
const r = new RouteResolver(
|
||||
new Map<RouteRef, string>([
|
||||
[legacyRef1, 'my-route'],
|
||||
[legacyRef2, 'my-parent/:x'],
|
||||
]),
|
||||
new Map([[legacyRef2, legacyRef1]]),
|
||||
[
|
||||
{
|
||||
routeRefs: new Set([legacyRef2]),
|
||||
path: 'my-parent/:x',
|
||||
...rest,
|
||||
children: [
|
||||
MATCH_ALL_ROUTE,
|
||||
{ routeRefs: new Set([legacyRef1]), path: 'my-route', ...rest },
|
||||
],
|
||||
},
|
||||
],
|
||||
new Map<ExternalRouteRef, RouteRef | SubRouteRef>([
|
||||
[legacyExternalRef1, legacyRef1],
|
||||
[legacyExternalRef2, legacySubRef3],
|
||||
]),
|
||||
'',
|
||||
emptyResolver,
|
||||
new Map(),
|
||||
);
|
||||
|
||||
expect(r.resolve(legacyRef1, src('/'))?.()).toBe('/my-route');
|
||||
expect(r.resolve(legacyRef2, src('/'))?.({ x: '1x' })).toBe(
|
||||
'/my-route/my-parent/1x',
|
||||
);
|
||||
expect(r.resolve(legacySubRef1, src('/'))?.()).toBe('/my-route/foo');
|
||||
expect(r.resolve(legacySubRef2, src('/'))?.({ a: '2a' })).toBe(
|
||||
'/my-route/foo/2a',
|
||||
);
|
||||
expect(r.resolve(legacySubRef3, src('/'))?.({ x: '3x' })).toBe(
|
||||
'/my-route/my-parent/3x/bar',
|
||||
);
|
||||
expect(r.resolve(legacySubRef4, src('/'))?.({ x: '4x', a: '4a' })).toBe(
|
||||
'/my-route/my-parent/4x/bar/4a',
|
||||
);
|
||||
expect(r.resolve(legacyExternalRef1, src('/'))?.()).toBe('/my-route');
|
||||
expect(r.resolve(legacyExternalRef2, src('/'))?.({ x: '6x' })).toBe(
|
||||
'/my-route/my-parent/6x/bar',
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve the most specific match', () => {
|
||||
const r = new RouteResolver(
|
||||
new Map<RouteRef, string>([
|
||||
[legacyRef1, 'deep'],
|
||||
[legacyRef2, 'root/:x'],
|
||||
[legacyRef3, 'sub/:y'],
|
||||
]),
|
||||
new Map<RouteRef, RouteRef>([
|
||||
[legacyRef3, legacyRef2],
|
||||
[legacyRef1, legacyRef3],
|
||||
]),
|
||||
[
|
||||
{
|
||||
routeRefs: new Set([legacyRef2]),
|
||||
path: 'root/:x',
|
||||
...rest,
|
||||
children: [
|
||||
MATCH_ALL_ROUTE,
|
||||
{
|
||||
routeRefs: new Set([legacyRef3]),
|
||||
path: 'sub/:y',
|
||||
...rest,
|
||||
children: [
|
||||
MATCH_ALL_ROUTE,
|
||||
{
|
||||
routeRefs: new Set([legacyRef1]),
|
||||
path: 'deep',
|
||||
...rest,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
new Map<ExternalRouteRef, RouteRef | SubRouteRef>(),
|
||||
'',
|
||||
emptyResolver,
|
||||
new Map(),
|
||||
);
|
||||
|
||||
expect(r.resolve(legacyRef2, src('/'))?.({ x: 'x' })).toBe('/root/x');
|
||||
expect(r.resolve(legacyRef3, src('/root/x'))?.({ y: 'y' })).toBe(
|
||||
'/root/x/sub/y',
|
||||
);
|
||||
|
||||
expect(() => r.resolve(legacyRef1, src('/'))?.()).toThrow(
|
||||
/^Cannot route.*with parent.*as it has parameters$/,
|
||||
);
|
||||
expect(() => r.resolve(legacyRef1, src('/root/x'))?.()).toThrow(
|
||||
/^Cannot route.*with parent.*as it has parameters$/,
|
||||
);
|
||||
expect(r.resolve(legacyRef1, src('/root/x/sub/y'))?.()).toBe(
|
||||
'/root/x/sub/y/deep',
|
||||
);
|
||||
// Without the MATCH_ALL_ROUTE, we wouldn't properly match the route here
|
||||
expect(
|
||||
r.resolve(legacyRef1, src('/root/x/sub/y/any/nested/path/here'))?.(),
|
||||
).toBe('/root/x/sub/y/deep');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,10 +39,10 @@ describe('collectRouteIds', () => {
|
||||
const extRef = createExternalRouteRef();
|
||||
|
||||
expect(String(ref)).toMatch(
|
||||
/^RouteRef\{created at '.*collectRouteIds\.test\.ts.*'\}$/,
|
||||
/^routeRef\{id=undefined,at='.*collectRouteIds\.test\.ts.*'\}$/,
|
||||
);
|
||||
expect(String(extRef)).toMatch(
|
||||
/^ExternalRouteRef\{created at '.*collectRouteIds\.test\.ts.*'\}$/,
|
||||
/^externalRouteRef\{id=undefined,at='.*collectRouteIds\.test\.ts.*'\}$/,
|
||||
);
|
||||
|
||||
const collected = collectRouteIds(
|
||||
@@ -62,8 +62,12 @@ describe('collectRouteIds', () => {
|
||||
'test.extRef': extRef,
|
||||
});
|
||||
|
||||
expect(String(ref)).toBe('RouteRef{test.ref}');
|
||||
expect(String(extRef)).toBe('ExternalRouteRef{test.extRef}');
|
||||
expect(String(ref)).toMatch(
|
||||
/^routeRef\{id=test.ref,at='.*collectRouteIds\.test\.ts.*'\}$/,
|
||||
);
|
||||
expect(String(extRef)).toMatch(
|
||||
/^externalRouteRef\{id=test.extRef,at='.*collectRouteIds\.test\.ts.*'\}$/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should report duplicate route IDs', () => {
|
||||
|
||||
@@ -636,7 +636,7 @@ describe('discovery', () => {
|
||||
},
|
||||
),
|
||||
).toThrow(
|
||||
/Refused to resolve alias 'other.root' for RouteRef{created at 'at .*extractRouteInfoFromAppNode\.test\.ts:\d+:\d+'} as it points to a different plugin, the expected plugin is 'test' but the alias points to 'other'/,
|
||||
/Refused to resolve alias 'other.root' for routeRef{id=undefined,at='.*extractRouteInfoFromAppNode\.test\.ts:\d+:\d+'} as it points to a different plugin, the expected plugin is 'test' but the alias points to 'other'/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -662,7 +662,7 @@ describe('discovery', () => {
|
||||
},
|
||||
),
|
||||
).toThrow(
|
||||
/Alias loop detected for RouteRef{created at 'at .*extractRouteInfoFromAppNode\.test\.ts:\d+:\d+'}/,
|
||||
/Alias loop detected for routeRef{id=undefined,at='.*extractRouteInfoFromAppNode\.test\.ts:\d+:\d+'}/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user