packages/core-api: add configLoader option to App
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
import React, { ComponentType, FC } from 'react';
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
import { AppContextProvider } from './AppContext';
|
||||
import { BackstageApp, AppComponents } from './types';
|
||||
import { BackstageApp, AppComponents, AppConfigLoader } from './types';
|
||||
import { BackstagePlugin } from '../plugin';
|
||||
import { FeatureFlagsRegistryItem } from './FeatureFlags';
|
||||
import { featureFlagsApiRef } from '../apis/definitions';
|
||||
@@ -31,8 +31,11 @@ import {
|
||||
AppTheme,
|
||||
AppThemeSelector,
|
||||
appThemeApiRef,
|
||||
configApiRef,
|
||||
ConfigReader,
|
||||
} from '../apis';
|
||||
import { ApiAggregator } from '../apis/ApiAggregator';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
type FullAppOptions = {
|
||||
apis: ApiHolder;
|
||||
@@ -40,6 +43,7 @@ type FullAppOptions = {
|
||||
plugins: BackstagePlugin[];
|
||||
components: AppComponents;
|
||||
themes: AppTheme[];
|
||||
configLoader: AppConfigLoader;
|
||||
};
|
||||
|
||||
export class PrivateAppImpl implements BackstageApp {
|
||||
@@ -48,6 +52,7 @@ export class PrivateAppImpl implements BackstageApp {
|
||||
private readonly plugins: BackstagePlugin[];
|
||||
private readonly components: AppComponents;
|
||||
private readonly themes: AppTheme[];
|
||||
private readonly configLoader: AppConfigLoader;
|
||||
|
||||
constructor(options: FullAppOptions) {
|
||||
this.apis = options.apis;
|
||||
@@ -55,6 +60,7 @@ export class PrivateAppImpl implements BackstageApp {
|
||||
this.plugins = options.plugins;
|
||||
this.components = options.components;
|
||||
this.themes = options.themes;
|
||||
this.configLoader = options.configLoader;
|
||||
}
|
||||
|
||||
getApis(): ApiHolder {
|
||||
@@ -141,18 +147,32 @@ export class PrivateAppImpl implements BackstageApp {
|
||||
}
|
||||
|
||||
getProvider(): ComponentType<{}> {
|
||||
const appApis = ApiRegistry.from([
|
||||
[appThemeApiRef, AppThemeSelector.createWithStorage(this.themes)],
|
||||
]);
|
||||
const apis = new ApiAggregator(this.apis, appApis);
|
||||
const Provider: FC<{}> = ({ children }) => {
|
||||
const config = useAsync(this.configLoader);
|
||||
if (config.loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Provider: FC<{}> = ({ children }) => (
|
||||
<ApiProvider apis={apis}>
|
||||
<AppContextProvider app={this}>
|
||||
<AppThemeProvider>{children}</AppThemeProvider>
|
||||
</AppContextProvider>
|
||||
</ApiProvider>
|
||||
);
|
||||
let errorPage = undefined;
|
||||
if (config.error) {
|
||||
const { BootErrorPage } = this.components;
|
||||
errorPage = <BootErrorPage step="load-config" error={config.error} />;
|
||||
}
|
||||
|
||||
const appApis = ApiRegistry.from([
|
||||
[appThemeApiRef, AppThemeSelector.createWithStorage(this.themes)],
|
||||
[configApiRef, new ConfigReader(config.value ?? {})],
|
||||
]);
|
||||
const apis = new ApiAggregator(this.apis, appApis);
|
||||
|
||||
return (
|
||||
<ApiProvider apis={apis}>
|
||||
<AppContextProvider app={this}>
|
||||
<AppThemeProvider>{errorPage ?? children}</AppThemeProvider>
|
||||
</AppContextProvider>
|
||||
</ApiProvider>
|
||||
);
|
||||
};
|
||||
return Provider;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,26 @@ import { BackstagePlugin } from '../plugin';
|
||||
import { ApiHolder } from '../apis';
|
||||
import { AppTheme } from '../apis/definitions';
|
||||
|
||||
export type BootErrorPageProps = {
|
||||
step: 'load-config';
|
||||
error: Error;
|
||||
};
|
||||
|
||||
export type AppComponents = {
|
||||
NotFoundErrorPage: ComponentType<{}>;
|
||||
BootErrorPage: ComponentType<BootErrorPageProps>;
|
||||
};
|
||||
|
||||
/**
|
||||
* TBD
|
||||
*/
|
||||
export type AppConfig = any;
|
||||
|
||||
/**
|
||||
* A function that loads in the App config that will be accessible via the ConfigApi.
|
||||
*/
|
||||
export type AppConfigLoader = () => Promise<AppConfig>;
|
||||
|
||||
export type AppOptions = {
|
||||
/**
|
||||
* A holder of all APIs available in the app.
|
||||
@@ -68,6 +84,17 @@ export type AppOptions = {
|
||||
* ```
|
||||
*/
|
||||
themes?: AppTheme[];
|
||||
|
||||
/**
|
||||
* A function that loads in App configuration that will be accessible via
|
||||
* the ConfigApi.
|
||||
*
|
||||
* Defaults to an empty config.
|
||||
*
|
||||
* TODO(Rugvip): Omitting this should instead default to loading in configuration
|
||||
* that was packaged by the backstage-cli and default docker container boot script.
|
||||
*/
|
||||
configLoader?: AppConfigLoader;
|
||||
};
|
||||
|
||||
export type BackstageApp = {
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import privateExports, {
|
||||
AppOptions,
|
||||
ApiRegistry,
|
||||
defaultSystemIcons,
|
||||
BootErrorPageProps,
|
||||
} from '@backstage/core-api';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import { ErrorPage } from '../layout/ErrorPage';
|
||||
import { lightTheme, darkTheme } from '@backstage/theme';
|
||||
@@ -38,12 +40,25 @@ export function createApp(options?: AppOptions) {
|
||||
const DefaultNotFoundPage = () => (
|
||||
<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />
|
||||
);
|
||||
const DefaultBootErrorPage: FC<BootErrorPageProps> = ({ step, error }) => {
|
||||
let message = '';
|
||||
if (step === 'load-config') {
|
||||
message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;
|
||||
}
|
||||
// TODO: figure out a nicer way to handle routing on the error page, when it can be done.
|
||||
return (
|
||||
<Router>
|
||||
<ErrorPage status="501" statusMessage={message} />
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
const apis = options?.apis ?? ApiRegistry.from([]);
|
||||
const icons = { ...defaultSystemIcons, ...options?.icons };
|
||||
const plugins = options?.plugins ?? [];
|
||||
const components = {
|
||||
NotFoundErrorPage: DefaultNotFoundPage,
|
||||
BootErrorPage: DefaultBootErrorPage,
|
||||
...options?.components,
|
||||
};
|
||||
const themes = options?.themes ?? [
|
||||
@@ -60,8 +75,16 @@ export function createApp(options?: AppOptions) {
|
||||
theme: darkTheme,
|
||||
},
|
||||
];
|
||||
const configLoader = options?.configLoader ?? (async () => ({}));
|
||||
|
||||
const app = new PrivateAppImpl({ apis, icons, plugins, components, themes });
|
||||
const app = new PrivateAppImpl({
|
||||
apis,
|
||||
icons,
|
||||
plugins,
|
||||
components,
|
||||
themes,
|
||||
configLoader,
|
||||
});
|
||||
|
||||
app.verify();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user