frontend-app-api: add legacy app context and default APIs
Co-authored-by: Camila Belo <camilaibs@gmail.com> Co-authored-by: Philipp Hugenroth <philipph@spotify.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -33,11 +33,16 @@ import {
|
||||
} from './wiring/parameters';
|
||||
import { RoutingProvider } from './routing/RoutingContext';
|
||||
import {
|
||||
AnyApiFactory,
|
||||
ApiHolder,
|
||||
AppComponents,
|
||||
AppContext,
|
||||
appThemeApiRef,
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
IconComponent,
|
||||
RouteRef,
|
||||
BackstagePlugin as LegacyBackstagePlugin,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { getAvailablePlugins } from './wiring/discovery';
|
||||
import {
|
||||
@@ -51,11 +56,18 @@ import {
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { AppThemeProvider } from '../../core-app-api/src/app/AppThemeProvider';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { AppContextProvider } from '../../core-app-api/src/app/AppContext';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseUrlConfigs';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { themes } from '../../app-defaults/src/defaults/themes';
|
||||
import {
|
||||
apis as defaultApis,
|
||||
components as defaultComponents,
|
||||
icons as defaultIcons,
|
||||
themes as defaultThemes,
|
||||
} from '../../app-defaults/src/defaults';
|
||||
|
||||
/** @public */
|
||||
export function createApp(options: {
|
||||
@@ -70,11 +82,12 @@ export function createApp(options: {
|
||||
|
||||
const builtinExtensions = [CoreRouter, Core];
|
||||
const discoveredPlugins = getAvailablePlugins();
|
||||
const allPlugins = [...discoveredPlugins, ...options.plugins];
|
||||
|
||||
// pull in default extension instance from discovered packages
|
||||
// apply config to adjust default extension instances and add more
|
||||
const extensionParams = mergeExtensionParameters({
|
||||
sources: [...options.plugins, ...discoveredPlugins],
|
||||
sources: allPlugins,
|
||||
builtinExtensions,
|
||||
parameters: readAppExtensionParameters(appConfig),
|
||||
});
|
||||
@@ -150,6 +163,8 @@ export function createApp(options: {
|
||||
|
||||
const apiHolder = createApiHolder(coreInstance, appConfig);
|
||||
|
||||
const appContext = createLegacyAppContext(allPlugins);
|
||||
|
||||
return {
|
||||
createRoot() {
|
||||
const rootComponents = rootInstances
|
||||
@@ -162,19 +177,65 @@ export function createApp(options: {
|
||||
.filter(Boolean);
|
||||
return (
|
||||
<ApiProvider apis={apiHolder}>
|
||||
<AppThemeProvider>
|
||||
<RoutingProvider routePaths={routePaths}>
|
||||
{rootComponents.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</RoutingProvider>
|
||||
</AppThemeProvider>
|
||||
<AppContextProvider appContext={appContext}>
|
||||
<AppThemeProvider>
|
||||
<RoutingProvider routePaths={routePaths}>
|
||||
{rootComponents.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</RoutingProvider>
|
||||
</AppThemeProvider>
|
||||
</AppContextProvider>
|
||||
</ApiProvider>
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function toLegacyPlugin(plugin: BackstagePlugin): LegacyBackstagePlugin {
|
||||
const errorMsg = 'Not implemented in legacy plugin compatibility layer';
|
||||
const notImplemented = () => {
|
||||
throw new Error(errorMsg);
|
||||
};
|
||||
return {
|
||||
getId(): string {
|
||||
return plugin.id;
|
||||
},
|
||||
get routes(): never {
|
||||
throw new Error(errorMsg);
|
||||
},
|
||||
get externalRoutes(): never {
|
||||
throw new Error(errorMsg);
|
||||
},
|
||||
getApis: notImplemented,
|
||||
getFeatureFlags: notImplemented,
|
||||
provide: notImplemented,
|
||||
__experimentalReconfigure: notImplemented,
|
||||
};
|
||||
}
|
||||
|
||||
function createLegacyAppContext(plugins: BackstagePlugin[]): AppContext {
|
||||
return {
|
||||
getPlugins(): LegacyBackstagePlugin[] {
|
||||
return plugins.map(toLegacyPlugin);
|
||||
},
|
||||
|
||||
getSystemIcon(key: string): IconComponent | undefined {
|
||||
return key in defaultIcons
|
||||
? defaultIcons[key as keyof typeof defaultIcons]
|
||||
: undefined;
|
||||
},
|
||||
|
||||
getSystemIcons(): Record<string, IconComponent> {
|
||||
return defaultIcons;
|
||||
},
|
||||
|
||||
getComponents(): AppComponents {
|
||||
return defaultComponents;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createApiHolder(
|
||||
coreExtension: ExtensionInstance,
|
||||
configApi: ConfigApi,
|
||||
@@ -200,7 +261,7 @@ function createApiHolder(
|
||||
api: appThemeApiRef,
|
||||
deps: {},
|
||||
// TODO: add extension for registering themes
|
||||
factory: () => AppThemeSelector.createWithStorage(themes),
|
||||
factory: () => AppThemeSelector.createWithStorage(defaultThemes),
|
||||
});
|
||||
|
||||
factoryRegistry.register('static', {
|
||||
@@ -209,6 +270,15 @@ function createApiHolder(
|
||||
factory: () => configApi,
|
||||
});
|
||||
|
||||
// TODO: ship these as default extensions instead
|
||||
for (const factory of defaultApis as AnyApiFactory[]) {
|
||||
if (!factoryRegistry.register('app', factory)) {
|
||||
throw new Error(
|
||||
`Duplicate or forbidden API factory for ${factory.api} in app`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());
|
||||
|
||||
return new ApiResolver(factoryRegistry);
|
||||
|
||||
Reference in New Issue
Block a user