core-compat-api: move entity page conversion into convertLegacyApp
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
+24
-22
@@ -14,13 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiHolder, getComponentData } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
createFrontendPlugin,
|
||||
ExtensionDefinition,
|
||||
FrontendModule,
|
||||
FrontendPlugin,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
ApiHolder,
|
||||
getComponentData,
|
||||
BackstagePlugin as LegacyBackstagePlugin,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import React from 'react';
|
||||
import {
|
||||
EntityCardBlueprint,
|
||||
@@ -72,14 +71,18 @@ function invertFilter(ifFunc?: EntityFilter): EntityFilter {
|
||||
return (entity, ctx) => !ifFunc(entity, ctx);
|
||||
}
|
||||
|
||||
export function convertLegacyEntityPage(
|
||||
export function collectEntityPageContents(
|
||||
entityPageElement: React.JSX.Element,
|
||||
): (FrontendModule | FrontendPlugin)[] {
|
||||
context: {
|
||||
discoveryExtension(
|
||||
extension: ExtensionDefinition,
|
||||
plugin?: LegacyBackstagePlugin,
|
||||
): void;
|
||||
},
|
||||
) {
|
||||
let cardCounter = 1;
|
||||
let routeCounter = 1;
|
||||
|
||||
const collected: ExtensionDefinition[] = [];
|
||||
|
||||
function traverse(element: React.ReactNode, parentFilter?: EntityFilter) {
|
||||
if (!React.isValidElement(element)) {
|
||||
return;
|
||||
@@ -91,9 +94,9 @@ export function convertLegacyEntityPage(
|
||||
const mergedIf = allFilters(parentFilter, pageNode.if);
|
||||
|
||||
if (pageNode.path === '/') {
|
||||
collected.push(
|
||||
context.discoveryExtension(
|
||||
EntityCardBlueprint.makeWithOverrides({
|
||||
name: `discovered-entity-cards-${cardCounter++}`,
|
||||
name: `discovered-${cardCounter++}`,
|
||||
factory(originalFactory, { apis }) {
|
||||
return originalFactory({
|
||||
type: 'full',
|
||||
@@ -104,9 +107,11 @@ export function convertLegacyEntityPage(
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
collected.push(
|
||||
const name = `discovered-${routeCounter++}`;
|
||||
|
||||
context.discoveryExtension(
|
||||
EntityContentBlueprint.makeWithOverrides({
|
||||
name: `discovered-entity-route-${routeCounter++}`,
|
||||
name,
|
||||
factory(originalFactory, { apis }) {
|
||||
return originalFactory({
|
||||
defaultPath: pageNode.path,
|
||||
@@ -116,6 +121,10 @@ export function convertLegacyEntityPage(
|
||||
});
|
||||
},
|
||||
}),
|
||||
getComponentData<LegacyBackstagePlugin>(
|
||||
pageNode.children,
|
||||
'core.plugin',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -151,13 +160,6 @@ export function convertLegacyEntityPage(
|
||||
}
|
||||
|
||||
traverse(entityPageElement);
|
||||
|
||||
return [
|
||||
createFrontendPlugin({
|
||||
id: 'converted-orphan-entity-content',
|
||||
extensions: collected,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
type EntityRoute = {
|
||||
@@ -192,7 +194,7 @@ function wrapAsyncEntityFilter(
|
||||
if (!loggedError) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`convertLegacyEntityPage does not support async entity filters, skipping filter ${asyncFilter}`,
|
||||
`collectEntityPageContents does not support async entity filters, skipping filter ${asyncFilter}`,
|
||||
);
|
||||
loggedError = true;
|
||||
}
|
||||
@@ -30,6 +30,8 @@ import {
|
||||
createFrontendPlugin,
|
||||
ApiBlueprint,
|
||||
PageBlueprint,
|
||||
FrontendModule,
|
||||
createFrontendModule,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import React, { Children, ReactNode, isValidElement } from 'react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
@@ -38,6 +40,7 @@ import {
|
||||
convertLegacyRouteRefs,
|
||||
} from './convertLegacyRouteRef';
|
||||
import { compatWrapper } from './compatWrapper';
|
||||
import { collectEntityPageContents } from './collectEntityPageContents';
|
||||
|
||||
/*
|
||||
|
||||
@@ -102,7 +105,7 @@ function makeRoutingShimExtension(options: {
|
||||
});
|
||||
}
|
||||
|
||||
function visitRouteChildren(options: {
|
||||
export function visitRouteChildren(options: {
|
||||
children: ReactNode;
|
||||
parentExtensionId: string;
|
||||
context: {
|
||||
@@ -157,7 +160,10 @@ function visitRouteChildren(options: {
|
||||
/** @internal */
|
||||
export function collectLegacyRoutes(
|
||||
flatRoutesElement: JSX.Element,
|
||||
): FrontendPlugin[] {
|
||||
entityPage?: JSX.Element,
|
||||
): (FrontendPlugin | FrontendModule)[] {
|
||||
const output = new Array<FrontendPlugin | FrontendModule>();
|
||||
|
||||
const pluginExtensions = new Map<
|
||||
LegacyBackstagePlugin,
|
||||
ExtensionDefinition[]
|
||||
@@ -262,20 +268,59 @@ export function collectLegacyRoutes(
|
||||
},
|
||||
);
|
||||
|
||||
return Array.from(pluginExtensions).map(([plugin, extensions]) =>
|
||||
createFrontendPlugin({
|
||||
id: plugin.getId(),
|
||||
extensions: [
|
||||
...extensions,
|
||||
...Array.from(plugin.getApis()).map(factory =>
|
||||
ApiBlueprint.make({
|
||||
name: factory.api.id,
|
||||
params: { factory },
|
||||
}),
|
||||
),
|
||||
],
|
||||
routes: convertLegacyRouteRefs(plugin.routes ?? {}),
|
||||
externalRoutes: convertLegacyRouteRefs(plugin.externalRoutes ?? {}),
|
||||
}),
|
||||
);
|
||||
if (entityPage) {
|
||||
collectEntityPageContents(entityPage, {
|
||||
discoveryExtension(extension, plugin) {
|
||||
if (!plugin || plugin.getId() === 'catalog') {
|
||||
getPluginExtensions(orphanRoutesPlugin).push(extension);
|
||||
} else {
|
||||
getPluginExtensions(plugin).push(extension);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const extensions = new Array<ExtensionDefinition>();
|
||||
visitRouteChildren({
|
||||
children: entityPage,
|
||||
parentExtensionId: `page:catalog/entity`,
|
||||
context: {
|
||||
pluginId: 'catalog',
|
||||
extensions,
|
||||
getUniqueName,
|
||||
discoverPlugin(plugin) {
|
||||
if (plugin.getId() !== 'catalog') {
|
||||
getPluginExtensions(plugin);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
output.push(
|
||||
createFrontendModule({
|
||||
pluginId: 'catalog',
|
||||
extensions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
for (const [plugin, extensions] of pluginExtensions) {
|
||||
output.push(
|
||||
createFrontendPlugin({
|
||||
id: plugin.getId(),
|
||||
extensions: [
|
||||
...extensions,
|
||||
...Array.from(plugin.getApis()).map(factory =>
|
||||
ApiBlueprint.make({
|
||||
name: factory.api.id,
|
||||
params: { factory },
|
||||
}),
|
||||
),
|
||||
],
|
||||
routes: convertLegacyRouteRefs(plugin.routes ?? {}),
|
||||
externalRoutes: convertLegacyRouteRefs(plugin.externalRoutes ?? {}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -59,9 +59,31 @@ function selectChildren(
|
||||
});
|
||||
}
|
||||
|
||||
export interface ConvertLegacyAppOptions {
|
||||
/**
|
||||
* By providing an entity page element here it will be split up and converted
|
||||
* into individual extensions for the catalog plugin in the new frontend
|
||||
* system.
|
||||
*
|
||||
* In order to use this option the entity page and other catalog extensions
|
||||
* must be removed from the legacy app, and the catalog plugin for the new
|
||||
* frontend system must be installed instead.
|
||||
*
|
||||
* In order for this conversion to work the entity page must be a plain React
|
||||
* element tree without any component wrapping anywhere between the root
|
||||
* element and the `EntityLayout.Route` elements.
|
||||
*
|
||||
* When enabling this conversion you are likely to encounter duplicate entity
|
||||
* page content provided both via the old structure and the new plugins. Any
|
||||
* duplicate content needs to be removed from the old structure.
|
||||
*/
|
||||
entityPage?: React.JSX.Element;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function convertLegacyApp(
|
||||
rootElement: React.JSX.Element,
|
||||
options: ConvertLegacyAppOptions = {},
|
||||
): (FrontendPlugin | FrontendModule)[] {
|
||||
if (getComponentData(rootElement, 'core.type') === 'FlatRoutes') {
|
||||
return collectLegacyRoutes(rootElement);
|
||||
@@ -136,7 +158,7 @@ export function convertLegacyApp(
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
const collectedRoutes = collectLegacyRoutes(routesEl);
|
||||
const collectedRoutes = collectLegacyRoutes(routesEl, options?.entityPage);
|
||||
|
||||
return [
|
||||
...collectedRoutes,
|
||||
|
||||
@@ -19,7 +19,6 @@ export * from './apis';
|
||||
|
||||
export { convertLegacyApp } from './convertLegacyApp';
|
||||
export { convertLegacyAppOptions } from './convertLegacyAppOptions';
|
||||
export { convertLegacyEntityPage } from './convertLegacyEntityPage';
|
||||
export { convertLegacyPlugin } from './convertLegacyPlugin';
|
||||
export { convertLegacyPageExtension } from './convertLegacyPageExtension';
|
||||
export {
|
||||
|
||||
Reference in New Issue
Block a user