Merge pull request #25914 from backstage/rugvip/forwards-compat
core-compat-api: add support for converting from the new system to the old
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-compat-api': patch
|
||||
---
|
||||
|
||||
Both `compatWrapper` and `convertLegacyRouteRef` now support converting from the new system to the old.
|
||||
@@ -44,6 +44,24 @@ export function convertLegacyRouteRef<
|
||||
ref: ExternalRouteRef<TParams, TOptional>,
|
||||
): ExternalRouteRef_2<TParams, TOptional>;
|
||||
|
||||
// @public
|
||||
export function convertLegacyRouteRef<TParams extends AnyRouteRefParams>(
|
||||
ref: RouteRef_2<TParams>,
|
||||
): RouteRef<TParams>;
|
||||
|
||||
// @public
|
||||
export function convertLegacyRouteRef<TParams extends AnyRouteRefParams>(
|
||||
ref: SubRouteRef_2<TParams>,
|
||||
): SubRouteRef<TParams>;
|
||||
|
||||
// @public
|
||||
export function convertLegacyRouteRef<
|
||||
TParams extends AnyRouteRefParams,
|
||||
TOptional extends boolean,
|
||||
>(
|
||||
ref: ExternalRouteRef_2<TParams, TOptional>,
|
||||
): ExternalRouteRef<TParams, TOptional>;
|
||||
|
||||
// @public
|
||||
export function convertLegacyRouteRefs<
|
||||
TRefs extends {
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"@backstage/frontend-app-api": "workspace:^",
|
||||
"@backstage/frontend-test-utils": "workspace:^",
|
||||
"@backstage/plugin-catalog": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@oriflame/backstage-plugin-score-card": "^0.8.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^15.0.0"
|
||||
|
||||
@@ -50,7 +50,9 @@ const legacyPluginStore = getOrCreateGlobalSingleton(
|
||||
() => new WeakMap<NewBackstagePlugin, LegacyBackstagePlugin>(),
|
||||
);
|
||||
|
||||
function toLegacyPlugin(plugin: NewBackstagePlugin): LegacyBackstagePlugin {
|
||||
export function toLegacyPlugin(
|
||||
plugin: NewBackstagePlugin,
|
||||
): LegacyBackstagePlugin {
|
||||
let legacy = legacyPluginStore.get(plugin);
|
||||
if (legacy) {
|
||||
return legacy;
|
||||
|
||||
@@ -14,10 +14,155 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
ApiHolder,
|
||||
ApiRef,
|
||||
AppContext,
|
||||
useApp,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
AnyRouteRefParams,
|
||||
ComponentRef,
|
||||
ComponentsApi,
|
||||
CoreErrorBoundaryFallbackProps,
|
||||
CoreNotFoundErrorPageProps,
|
||||
CoreProgressProps,
|
||||
ExternalRouteRef,
|
||||
IconComponent,
|
||||
IconsApi,
|
||||
RouteFunc,
|
||||
RouteRef,
|
||||
RouteResolutionApi,
|
||||
RouteResolutionApiResolveOptions,
|
||||
SubRouteRef,
|
||||
componentsApiRef,
|
||||
coreComponentRefs,
|
||||
iconsApiRef,
|
||||
routeResolutionApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import React, { ComponentType, useMemo } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { toLegacyPlugin } from './BackwardsCompatProvider';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { ApiProvider } from '../../../core-app-api/src/apis/system/ApiProvider';
|
||||
import { useVersionedContext } from '@backstage/version-bridge';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { type RouteResolver } from '../../../core-plugin-api/src/routing/useRouteRef';
|
||||
import { convertLegacyRouteRef } from '../convertLegacyRouteRef';
|
||||
|
||||
class CompatComponentsApi implements ComponentsApi {
|
||||
readonly #Progress: ComponentType<CoreProgressProps>;
|
||||
readonly #NotFoundErrorPage: ComponentType<CoreNotFoundErrorPageProps>;
|
||||
readonly #ErrorBoundaryFallback: ComponentType<CoreErrorBoundaryFallbackProps>;
|
||||
|
||||
constructor(app: AppContext) {
|
||||
const components = app.getComponents();
|
||||
const ErrorBoundaryFallback = (props: CoreErrorBoundaryFallbackProps) => (
|
||||
<components.ErrorBoundaryFallback
|
||||
{...props}
|
||||
plugin={props.plugin && toLegacyPlugin(props.plugin)}
|
||||
/>
|
||||
);
|
||||
this.#Progress = components.Progress;
|
||||
this.#NotFoundErrorPage = components.NotFoundErrorPage;
|
||||
this.#ErrorBoundaryFallback = ErrorBoundaryFallback;
|
||||
}
|
||||
|
||||
getComponent<T extends {}>(ref: ComponentRef<T>): ComponentType<T> {
|
||||
switch (ref.id) {
|
||||
case coreComponentRefs.progress.id:
|
||||
return this.#Progress as ComponentType<any>;
|
||||
case coreComponentRefs.notFoundErrorPage.id:
|
||||
return this.#NotFoundErrorPage as ComponentType<any>;
|
||||
case coreComponentRefs.errorBoundaryFallback.id:
|
||||
return this.#ErrorBoundaryFallback as ComponentType<any>;
|
||||
default:
|
||||
throw new Error(
|
||||
`No backwards compatible component is available for ref '${ref.id}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CompatIconsApi implements IconsApi {
|
||||
readonly #app: AppContext;
|
||||
|
||||
constructor(app: AppContext) {
|
||||
this.#app = app;
|
||||
}
|
||||
|
||||
getIcon(key: string): IconComponent | undefined {
|
||||
return this.#app.getSystemIcon(key);
|
||||
}
|
||||
|
||||
listIconKeys(): string[] {
|
||||
return Object.keys(this.#app.getSystemIcons());
|
||||
}
|
||||
}
|
||||
|
||||
class CompatRouteResolutionApi implements RouteResolutionApi {
|
||||
readonly #routeResolver: RouteResolver;
|
||||
|
||||
constructor(routeResolver: RouteResolver) {
|
||||
this.#routeResolver = routeResolver;
|
||||
}
|
||||
|
||||
resolve<TParams extends AnyRouteRefParams>(
|
||||
anyRouteRef:
|
||||
| RouteRef<TParams>
|
||||
| SubRouteRef<TParams>
|
||||
| ExternalRouteRef<TParams, any>,
|
||||
options?: RouteResolutionApiResolveOptions | undefined,
|
||||
): RouteFunc<TParams> | undefined {
|
||||
const legacyRef = convertLegacyRouteRef(anyRouteRef as RouteRef<TParams>);
|
||||
return this.#routeResolver.resolve(legacyRef, options?.sourcePath ?? '/');
|
||||
}
|
||||
}
|
||||
|
||||
class ForwardsCompatApis implements ApiHolder {
|
||||
readonly #componentsApi: ComponentsApi;
|
||||
readonly #iconsApi: IconsApi;
|
||||
readonly #routeResolutionApi: RouteResolutionApi;
|
||||
|
||||
constructor(app: AppContext, routeResolver: RouteResolver) {
|
||||
this.#componentsApi = new CompatComponentsApi(app);
|
||||
this.#iconsApi = new CompatIconsApi(app);
|
||||
this.#routeResolutionApi = new CompatRouteResolutionApi(routeResolver);
|
||||
}
|
||||
|
||||
get<T>(ref: ApiRef<any>): T | undefined {
|
||||
if (ref.id === componentsApiRef.id) {
|
||||
return this.#componentsApi as T;
|
||||
} else if (ref.id === iconsApiRef.id) {
|
||||
return this.#iconsApi as T;
|
||||
} else if (ref.id === routeResolutionApiRef.id) {
|
||||
return this.#routeResolutionApi as T;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function NewAppApisProvider(props: { children: ReactNode }) {
|
||||
const app = useApp();
|
||||
const versionedRouteResolverContext = useVersionedContext<{
|
||||
1: RouteResolver;
|
||||
}>('routing-context');
|
||||
if (!versionedRouteResolverContext) {
|
||||
throw new Error('Routing context is not available');
|
||||
}
|
||||
const routeResolver = versionedRouteResolverContext.atVersion(1);
|
||||
if (!routeResolver) {
|
||||
throw new Error('RoutingContext v1 not available');
|
||||
}
|
||||
|
||||
const appFallbackApis = useMemo(
|
||||
() => new ForwardsCompatApis(app, routeResolver),
|
||||
[app, routeResolver],
|
||||
);
|
||||
|
||||
return <ApiProvider apis={appFallbackApis}>{props.children}</ApiProvider>;
|
||||
}
|
||||
|
||||
export function ForwardsCompatProvider(props: { children: ReactNode }) {
|
||||
// TODO(Rugvip): Implement
|
||||
return <>{props.children}</>;
|
||||
return <NewAppApisProvider>{props.children}</NewAppApisProvider>;
|
||||
}
|
||||
|
||||
@@ -16,21 +16,28 @@
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
componentsApiRef,
|
||||
coreComponentRefs,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
iconsApiRef,
|
||||
useRouteRef as useNewRouteRef,
|
||||
createRouteRef as createNewRouteRef,
|
||||
useApi,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createExtensionTester,
|
||||
renderInTestApp,
|
||||
renderInTestApp as renderInNewTestApp,
|
||||
} from '@backstage/frontend-test-utils';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { compatWrapper } from './compatWrapper';
|
||||
import {
|
||||
createRouteRef,
|
||||
useApp,
|
||||
useRouteRef,
|
||||
useRouteRef as useOldRouteRef,
|
||||
createRouteRef as createOldRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { convertLegacyRouteRef } from '../convertLegacyRouteRef';
|
||||
import { renderInTestApp as renderInOldTestApp } from '@backstage/test-utils';
|
||||
|
||||
describe('BackwardsCompatProvider', () => {
|
||||
it('should convert the app context', () => {
|
||||
@@ -74,14 +81,58 @@ describe('BackwardsCompatProvider', () => {
|
||||
});
|
||||
|
||||
it('should convert the routing context', () => {
|
||||
const routeRef = createRouteRef({ id: 'test' });
|
||||
const routeRef = createOldRouteRef({ id: 'test' });
|
||||
|
||||
function Component() {
|
||||
const link = useRouteRef(routeRef);
|
||||
const link = useOldRouteRef(routeRef);
|
||||
return <div>link: {link()}</div>;
|
||||
}
|
||||
|
||||
renderInTestApp(compatWrapper(<Component />), {
|
||||
renderInNewTestApp(compatWrapper(<Component />), {
|
||||
mountedRoutes: { '/test': convertLegacyRouteRef(routeRef) },
|
||||
});
|
||||
|
||||
expect(screen.getByText('link: /test')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ForwardsCompatProvider', () => {
|
||||
it('should convert the app context', async () => {
|
||||
function Component() {
|
||||
const components = useApi(componentsApiRef);
|
||||
const icons = useApi(iconsApiRef);
|
||||
return (
|
||||
<div data-testid="ctx">
|
||||
components:{' '}
|
||||
{Object.entries(coreComponentRefs)
|
||||
.map(
|
||||
([name, ref]) =>
|
||||
`${name}=${Boolean(components.getComponent(ref))}`,
|
||||
)
|
||||
.join(', ')}
|
||||
{'\n'}
|
||||
icons: {icons.listIconKeys().join(', ')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
await renderInOldTestApp(compatWrapper(<Component />));
|
||||
|
||||
expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(`
|
||||
"components: progress=true, notFoundErrorPage=true, errorBoundaryFallback=true
|
||||
icons: kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, user, warning"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should convert the routing context', async () => {
|
||||
const routeRef = createNewRouteRef();
|
||||
|
||||
function Component() {
|
||||
const link = useNewRouteRef(routeRef);
|
||||
return <div>link: {link()}</div>;
|
||||
}
|
||||
|
||||
await renderInOldTestApp(compatWrapper(<Component />), {
|
||||
mountedRoutes: { '/test': convertLegacyRouteRef(routeRef) },
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
RouteRef as OldRouteRef,
|
||||
SubRouteRef as OldSubRouteRef,
|
||||
ExternalRouteRef as OldExternalRouteRef,
|
||||
createRouteRef as createOldRouteRef,
|
||||
createSubRouteRef as createOldSubRouteRef,
|
||||
createExternalRouteRef as createOldExternalRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
RouteRef as NewRouteRef,
|
||||
SubRouteRef as NewSubRouteRef,
|
||||
ExternalRouteRef as NewExternalRouteRef,
|
||||
createRouteRef as createNewRouteRef,
|
||||
createSubRouteRef as createNewSubRouteRef,
|
||||
createExternalRouteRef as createNewExternalRouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { convertLegacyRouteRef } from './convertLegacyRouteRef';
|
||||
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { toInternalRouteRef as toInternalNewRouteRef } from '../../frontend-plugin-api/src/routing/RouteRef';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { toInternalSubRouteRef as toInternalNewSubRouteRef } from '../../frontend-plugin-api/src/routing/SubRouteRef';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { toInternalExternalRouteRef as toInternalNewExternalRouteRef } from '../../frontend-plugin-api/src/routing/ExternalRouteRef';
|
||||
|
||||
describe('convertLegacyRouteRef', () => {
|
||||
it('converts old to new', () => {
|
||||
const ref1 = createOldRouteRef({ id: 'ref1' });
|
||||
const ref2 = createOldRouteRef({ id: 'ref2', params: ['p1', 'p2'] });
|
||||
const ref1sub1 = createOldSubRouteRef({
|
||||
id: 'sub1',
|
||||
parent: ref1,
|
||||
path: '/sub1',
|
||||
});
|
||||
const ref1sub2 = createOldSubRouteRef({
|
||||
id: 'sub2',
|
||||
parent: ref1,
|
||||
path: '/sub2/:p3',
|
||||
});
|
||||
const ref2sub1 = createOldSubRouteRef({
|
||||
id: 'sub1',
|
||||
parent: ref2,
|
||||
path: '/sub1/:p3',
|
||||
});
|
||||
const ref3 = createOldExternalRouteRef({
|
||||
id: 'ref3',
|
||||
});
|
||||
const ref4 = createOldExternalRouteRef({
|
||||
id: 'ref4',
|
||||
optional: true,
|
||||
defaultTarget: 'ref2',
|
||||
params: ['p1', 'p2'],
|
||||
});
|
||||
|
||||
const ref1Converted: NewRouteRef = convertLegacyRouteRef(ref1);
|
||||
const ref2Converted: NewRouteRef = convertLegacyRouteRef(ref2);
|
||||
const ref1sub1Converted: NewSubRouteRef = convertLegacyRouteRef(ref1sub1);
|
||||
const ref1sub2Converted: NewSubRouteRef = convertLegacyRouteRef(ref1sub2);
|
||||
const ref2sub1Converted: NewSubRouteRef = convertLegacyRouteRef(ref2sub1);
|
||||
const ref3Converted: NewExternalRouteRef = convertLegacyRouteRef(ref3);
|
||||
const ref4Converted: NewExternalRouteRef = convertLegacyRouteRef(ref4);
|
||||
|
||||
// Check for reference equality
|
||||
expect(ref1).toBe(ref1Converted);
|
||||
expect(ref2).toBe(ref2Converted);
|
||||
expect(ref1sub1).toBe(ref1sub1Converted);
|
||||
expect(ref1sub2).toBe(ref1sub2Converted);
|
||||
expect(ref2sub1).toBe(ref2sub1Converted);
|
||||
expect(ref3).toBe(ref3Converted);
|
||||
expect(ref4).toBe(ref4Converted);
|
||||
|
||||
const ref1Internal = toInternalNewRouteRef(ref1Converted);
|
||||
const ref2Internal = toInternalNewRouteRef(ref2Converted);
|
||||
const ref1sub1Internal = toInternalNewSubRouteRef(ref1sub1Converted);
|
||||
const ref1sub2Internal = toInternalNewSubRouteRef(ref1sub2Converted);
|
||||
const ref2sub1Internal = toInternalNewSubRouteRef(ref2sub1Converted);
|
||||
const ref3Internal = toInternalNewExternalRouteRef(ref3Converted);
|
||||
const ref4Internal = toInternalNewExternalRouteRef(ref4Converted);
|
||||
|
||||
expect(ref1Internal.getDescription()).toBe(
|
||||
'routeRef{type=absolute,id=ref1}',
|
||||
);
|
||||
expect(ref1Internal.getParams()).toEqual([]);
|
||||
expect(ref2Internal.getDescription()).toBe(
|
||||
'routeRef{type=absolute,id=ref2}',
|
||||
);
|
||||
expect(ref2Internal.getParams()).toEqual(['p1', 'p2']);
|
||||
|
||||
expect(ref1sub1Internal.getDescription()).toBe(
|
||||
'routeRef{type=sub,id=sub1}',
|
||||
);
|
||||
expect(ref1sub1Internal.getParams()).toEqual([]);
|
||||
expect(ref1sub1Internal.getParent()).toBe(ref1);
|
||||
expect(ref1sub2Internal.getDescription()).toBe(
|
||||
'routeRef{type=sub,id=sub2}',
|
||||
);
|
||||
expect(ref1sub2Internal.getParams()).toEqual(['p3']);
|
||||
expect(ref1sub2Internal.getParent()).toBe(ref1);
|
||||
expect(ref2sub1Internal.getDescription()).toBe(
|
||||
'routeRef{type=sub,id=sub1}',
|
||||
);
|
||||
expect(ref2sub1Internal.getParams()).toEqual(['p1', 'p2', 'p3']);
|
||||
expect(ref2sub1Internal.getParent()).toBe(ref2);
|
||||
|
||||
expect(ref3Internal.getDefaultTarget()).toBe(undefined);
|
||||
expect(ref3Internal.getDescription()).toBe(
|
||||
'routeRef{type=external,id=ref3}',
|
||||
);
|
||||
expect(ref3Internal.getParams()).toEqual([]);
|
||||
expect(ref3Internal.optional).toBe(false);
|
||||
expect(ref4Internal.getDefaultTarget()).toBe('ref2');
|
||||
expect(ref4Internal.getDescription()).toBe(
|
||||
'routeRef{type=external,id=ref4}',
|
||||
);
|
||||
expect(ref4Internal.getParams()).toEqual(['p1', 'p2']);
|
||||
expect(ref4Internal.optional).toBe(true);
|
||||
});
|
||||
|
||||
it('converts new to old', () => {
|
||||
const ref1 = createNewRouteRef();
|
||||
const ref2 = createNewRouteRef({ params: ['p1', 'p2'] });
|
||||
const ref1sub1 = createNewSubRouteRef({
|
||||
parent: ref1,
|
||||
path: '/sub1',
|
||||
});
|
||||
const ref1sub2 = createNewSubRouteRef({
|
||||
parent: ref1,
|
||||
path: '/sub2/:p3',
|
||||
});
|
||||
const ref2sub1 = createNewSubRouteRef({
|
||||
parent: ref2,
|
||||
path: '/sub1/:p3',
|
||||
});
|
||||
const ref3 = createNewExternalRouteRef();
|
||||
const ref4 = createNewExternalRouteRef({
|
||||
optional: true,
|
||||
defaultTarget: 'ref2',
|
||||
params: ['p1', 'p2'],
|
||||
});
|
||||
|
||||
const ref1Converted: OldRouteRef = convertLegacyRouteRef(ref1);
|
||||
const ref2Converted: OldRouteRef = convertLegacyRouteRef(ref2);
|
||||
const ref1sub1Converted: OldSubRouteRef = convertLegacyRouteRef(ref1sub1);
|
||||
const ref1sub2Converted: OldSubRouteRef = convertLegacyRouteRef(ref1sub2);
|
||||
const ref2sub1Converted: OldSubRouteRef = convertLegacyRouteRef(ref2sub1);
|
||||
const ref3Converted: OldExternalRouteRef = convertLegacyRouteRef(ref3);
|
||||
const ref4Converted: OldExternalRouteRef = convertLegacyRouteRef(ref4);
|
||||
|
||||
// Check for reference equality
|
||||
expect(ref1).toBe(ref1Converted);
|
||||
expect(ref2).toBe(ref2Converted);
|
||||
expect(ref1sub1).toBe(ref1sub1Converted);
|
||||
expect(ref1sub2).toBe(ref1sub2Converted);
|
||||
expect(ref2sub1).toBe(ref2sub1Converted);
|
||||
expect(ref3).toBe(ref3Converted);
|
||||
expect(ref4).toBe(ref4Converted);
|
||||
|
||||
expect(String(ref1Converted)).toMatch(/^RouteRef\{created at '.*'\}$/);
|
||||
expect(ref1Converted.params).toEqual([]);
|
||||
expect(String(ref2Converted)).toMatch(/^RouteRef\{created at '.*'\}$/);
|
||||
expect(ref2Converted.params).toEqual(['p1', 'p2']);
|
||||
|
||||
expect(String(ref1sub1Converted)).toMatch(
|
||||
/^SubRouteRef\{at \/sub1 with parent created at '.*'\}$/,
|
||||
);
|
||||
expect(ref1sub1Converted.params).toEqual([]);
|
||||
expect(ref1sub1Converted.parent).toBe(ref1);
|
||||
expect(String(ref1sub2Converted)).toMatch(
|
||||
/^SubRouteRef\{at \/sub2\/:p3 with parent created at '.*'\}$/,
|
||||
);
|
||||
expect(ref1sub2Converted.params).toEqual(['p3']);
|
||||
expect(ref1sub2Converted.parent).toBe(ref1);
|
||||
expect(String(ref2sub1Converted)).toMatch(
|
||||
/^SubRouteRef\{at \/sub1\/:p3 with parent created at '.*'\}$/,
|
||||
);
|
||||
expect(ref2sub1Converted.params).toEqual(['p1', 'p2', 'p3']);
|
||||
expect(ref2sub1Converted.parent).toBe(ref2);
|
||||
|
||||
expect(String(ref3Converted)).toMatch(
|
||||
/^ExternalRouteRef\{created at '.*'\}$/,
|
||||
);
|
||||
expect(ref3Converted.params).toEqual([]);
|
||||
expect(ref3Converted.optional).toBe(false);
|
||||
expect(String(ref4Converted)).toMatch(
|
||||
/^ExternalRouteRef\{created at '.*'\}$/,
|
||||
);
|
||||
expect(ref4Converted.params).toEqual(['p1', 'p2']);
|
||||
expect(ref4Converted.optional).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -116,16 +116,113 @@ export function convertLegacyRouteRef<
|
||||
ref: LegacyExternalRouteRef<TParams, TOptional>,
|
||||
): ExternalRouteRef<TParams, TOptional>;
|
||||
|
||||
/**
|
||||
* A temporary helper to convert a new route ref to the legacy system.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* In the future the legacy createRouteRef will instead create refs compatible with both systems.
|
||||
*/
|
||||
export function convertLegacyRouteRef<TParams extends AnyRouteRefParams>(
|
||||
ref: RouteRef<TParams>,
|
||||
): LegacyRouteRef<TParams>;
|
||||
|
||||
/**
|
||||
* A temporary helper to convert a new sub route ref to the legacy system.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* In the future the legacy createSubRouteRef will instead create refs compatible with both systems.
|
||||
*/
|
||||
export function convertLegacyRouteRef<TParams extends AnyRouteRefParams>(
|
||||
ref: SubRouteRef<TParams>,
|
||||
): LegacySubRouteRef<TParams>;
|
||||
|
||||
/**
|
||||
* A temporary helper to convert a new external route ref to the legacy system.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* In the future the legacy createExternalRouteRef will instead create refs compatible with both systems.
|
||||
*/
|
||||
export function convertLegacyRouteRef<
|
||||
TParams extends AnyRouteRefParams,
|
||||
TOptional extends boolean,
|
||||
>(
|
||||
ref: ExternalRouteRef<TParams, TOptional>,
|
||||
): LegacyExternalRouteRef<TParams, TOptional>;
|
||||
export function convertLegacyRouteRef(
|
||||
ref: LegacyRouteRef | LegacySubRouteRef | LegacyExternalRouteRef,
|
||||
): RouteRef | SubRouteRef | ExternalRouteRef {
|
||||
ref:
|
||||
| LegacyRouteRef
|
||||
| LegacySubRouteRef
|
||||
| LegacyExternalRouteRef
|
||||
| RouteRef
|
||||
| SubRouteRef
|
||||
| ExternalRouteRef,
|
||||
):
|
||||
| RouteRef
|
||||
| SubRouteRef
|
||||
| ExternalRouteRef
|
||||
| LegacyRouteRef
|
||||
| LegacySubRouteRef
|
||||
| LegacyExternalRouteRef {
|
||||
const isNew = '$$type' in ref;
|
||||
const oldType = (ref as unknown as { [routeRefType]: unknown })[routeRefType];
|
||||
|
||||
// Ref has already been converted
|
||||
if ('$$type' in ref) {
|
||||
return ref as unknown as RouteRef | SubRouteRef | ExternalRouteRef;
|
||||
if (isNew && oldType) {
|
||||
return ref as any;
|
||||
}
|
||||
|
||||
const type = (ref as unknown as { [routeRefType]: unknown })[routeRefType];
|
||||
if (isNew) {
|
||||
return convertNewToOld(
|
||||
ref as unknown as RouteRef | SubRouteRef | ExternalRouteRef,
|
||||
);
|
||||
}
|
||||
|
||||
return convertOldToNew(ref, oldType);
|
||||
}
|
||||
|
||||
function convertNewToOld(
|
||||
ref: RouteRef | SubRouteRef | ExternalRouteRef,
|
||||
): LegacyRouteRef | LegacySubRouteRef | LegacyExternalRouteRef {
|
||||
if (ref.$$type === '@backstage/RouteRef') {
|
||||
const newRef = toInternalRouteRef(ref);
|
||||
return Object.assign(ref, {
|
||||
[routeRefType]: 'absolute',
|
||||
params: newRef.getParams(),
|
||||
title: newRef.getDescription(),
|
||||
} as Omit<LegacyRouteRef, '$$routeRefType'>) as unknown as LegacyRouteRef;
|
||||
}
|
||||
if (ref.$$type === '@backstage/SubRouteRef') {
|
||||
const newRef = toInternalSubRouteRef(ref);
|
||||
return Object.assign(ref, {
|
||||
[routeRefType]: 'sub',
|
||||
parent: convertLegacyRouteRef(newRef.getParent()),
|
||||
params: newRef.getParams(),
|
||||
} as Omit<LegacySubRouteRef, '$$routeRefType' | 'path'>) as unknown as LegacySubRouteRef;
|
||||
}
|
||||
if (ref.$$type === '@backstage/ExternalRouteRef') {
|
||||
const newRef = toInternalExternalRouteRef(ref);
|
||||
return Object.assign(ref, {
|
||||
[routeRefType]: 'external',
|
||||
params: newRef.getParams(),
|
||||
defaultTarget: newRef.getDefaultTarget(),
|
||||
} as Omit<LegacyExternalRouteRef, '$$routeRefType' | 'optional'>) as unknown as LegacyExternalRouteRef;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Failed to convert route ref, unknown type '${(ref as any).$$type}'`,
|
||||
);
|
||||
}
|
||||
|
||||
function convertOldToNew(
|
||||
ref: LegacyRouteRef | LegacySubRouteRef | LegacyExternalRouteRef,
|
||||
type: unknown,
|
||||
): RouteRef | SubRouteRef | ExternalRouteRef {
|
||||
if (type === 'absolute') {
|
||||
const legacyRef = ref as LegacyRouteRef;
|
||||
const legacyRefStr = String(legacyRef);
|
||||
|
||||
@@ -4171,6 +4171,7 @@ __metadata:
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/plugin-catalog": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/version-bridge": "workspace:^"
|
||||
"@oriflame/backstage-plugin-score-card": ^0.8.0
|
||||
"@testing-library/jest-dom": ^6.0.0
|
||||
|
||||
Reference in New Issue
Block a user