frontend-defaults: removed deprecated CreateAppFeatureLoader
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-defaults': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed the deprecated `CreateAppFeatureLoader` and support for it in other APIs. Switch existing usage to use the newer `createFrontendFeatureLoader` from `@backstage/frontend-plugin-api` instead.
|
||||
@@ -18,14 +18,6 @@ export function createApp(options?: CreateAppOptions): {
|
||||
createRoot(): JSX_2.Element;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export interface CreateAppFeatureLoader {
|
||||
getLoaderName(): string;
|
||||
load(options: { config: ConfigApi }): Promise<{
|
||||
features: FrontendFeature[];
|
||||
}>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface CreateAppOptions {
|
||||
// (undocumented)
|
||||
@@ -39,11 +31,7 @@ export interface CreateAppOptions {
|
||||
| ExtensionFactoryMiddleware
|
||||
| ExtensionFactoryMiddleware[];
|
||||
// (undocumented)
|
||||
features?: (
|
||||
| FrontendFeature
|
||||
| FrontendFeatureLoader
|
||||
| CreateAppFeatureLoader
|
||||
)[];
|
||||
features?: (FrontendFeature | FrontendFeatureLoader)[];
|
||||
// (undocumented)
|
||||
flags?: {
|
||||
allowUnknownExtensionConfig?: boolean;
|
||||
@@ -66,11 +54,7 @@ export function discoverAvailableFeatures(config: Config): {
|
||||
// @public (undocumented)
|
||||
export function resolveAsyncFeatures(options: {
|
||||
config: Config;
|
||||
features?: (
|
||||
| FrontendFeature
|
||||
| FrontendFeatureLoader
|
||||
| CreateAppFeatureLoader
|
||||
)[];
|
||||
features?: (FrontendFeature | FrontendFeatureLoader)[];
|
||||
}): Promise<{
|
||||
features: FrontendFeature[];
|
||||
}>;
|
||||
|
||||
@@ -21,13 +21,14 @@ import {
|
||||
createExtension,
|
||||
PageBlueprint,
|
||||
createFrontendPlugin,
|
||||
createFrontendFeatureLoader,
|
||||
ThemeBlueprint,
|
||||
createFrontendModule,
|
||||
useAppNode,
|
||||
FrontendPluginInfo,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { CreateAppFeatureLoader, createApp } from './createApp';
|
||||
import { createApp } from './createApp';
|
||||
import { mockApis, renderWithEffects } from '@backstage/test-utils';
|
||||
import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { default as appPluginOriginal } from '@backstage/plugin-app';
|
||||
@@ -169,28 +170,21 @@ describe('createApp', () => {
|
||||
});
|
||||
|
||||
it('should support feature loaders', async () => {
|
||||
const loader: CreateAppFeatureLoader = {
|
||||
getLoaderName() {
|
||||
return 'test-loader';
|
||||
},
|
||||
async load({ config }) {
|
||||
return {
|
||||
features: [
|
||||
createFrontendPlugin({
|
||||
pluginId: 'test',
|
||||
extensions: [
|
||||
PageBlueprint.make({
|
||||
params: {
|
||||
defaultPath: '/',
|
||||
loader: async () => <div>{config.getString('key')}</div>,
|
||||
},
|
||||
}),
|
||||
],
|
||||
const loader = createFrontendFeatureLoader({
|
||||
async *loader({ config }) {
|
||||
yield createFrontendPlugin({
|
||||
pluginId: 'test',
|
||||
extensions: [
|
||||
PageBlueprint.make({
|
||||
params: {
|
||||
defaultPath: '/',
|
||||
loader: async () => <div>{config.getString('key')}</div>,
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
configLoader: async () => ({
|
||||
@@ -207,14 +201,11 @@ describe('createApp', () => {
|
||||
});
|
||||
|
||||
it('should propagate errors thrown by feature loaders', async () => {
|
||||
const loader: CreateAppFeatureLoader = {
|
||||
getLoaderName() {
|
||||
return 'test-loader';
|
||||
},
|
||||
async load() {
|
||||
const loader = createFrontendFeatureLoader({
|
||||
async loader() {
|
||||
throw new TypeError('boom');
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
configLoader: async () => ({
|
||||
@@ -223,10 +214,8 @@ describe('createApp', () => {
|
||||
features: [loader],
|
||||
});
|
||||
|
||||
await expect(
|
||||
renderWithEffects(app.createRoot()),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Failed to read frontend features from loader 'test-loader', TypeError: boom"`,
|
||||
await expect(renderWithEffects(app.createRoot())).rejects.toThrow(
|
||||
/Failed to read frontend features from loader created at '.*\/createApp\.test\.tsx:\d+:\d+': TypeError: boom/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -36,37 +36,13 @@ import appPlugin from '@backstage/plugin-app';
|
||||
import { discoverAvailableFeatures } from './discovery';
|
||||
import { resolveAsyncFeatures } from './resolution';
|
||||
|
||||
/**
|
||||
* A source of dynamically loaded frontend features.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use the {@link @backstage/frontend-plugin-api#createFrontendFeatureLoader} function instead.
|
||||
*/
|
||||
export interface CreateAppFeatureLoader {
|
||||
/**
|
||||
* Returns name of this loader. suitable for showing to users.
|
||||
*/
|
||||
getLoaderName(): string;
|
||||
|
||||
/**
|
||||
* Loads a number of features dynamically.
|
||||
*/
|
||||
load(options: { config: ConfigApi }): Promise<{
|
||||
features: FrontendFeature[];
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@link createApp}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface CreateAppOptions {
|
||||
features?: (
|
||||
| FrontendFeature
|
||||
| FrontendFeatureLoader
|
||||
| CreateAppFeatureLoader
|
||||
)[];
|
||||
features?: (FrontendFeature | FrontendFeatureLoader)[];
|
||||
configLoader?: () => Promise<{ config: ConfigApi }>;
|
||||
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
|
||||
/**
|
||||
|
||||
@@ -20,11 +20,7 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export {
|
||||
createApp,
|
||||
type CreateAppOptions,
|
||||
type CreateAppFeatureLoader,
|
||||
} from './createApp';
|
||||
export { createApp, type CreateAppOptions } from './createApp';
|
||||
export { createPublicSignInApp } from './createPublicSignInApp';
|
||||
export { discoverAvailableFeatures } from './discovery';
|
||||
export { resolveAsyncFeatures } from './resolution';
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
FrontendFeatureLoader,
|
||||
PageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { CreateAppFeatureLoader } from './createApp';
|
||||
import { resolveAsyncFeatures } from './resolution';
|
||||
import { mockApis } from '@backstage/test-utils';
|
||||
|
||||
@@ -71,75 +70,6 @@ describe('resolveAsyncFeatures', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('supports deprecated feature loaders', async () => {
|
||||
const loader: CreateAppFeatureLoader = {
|
||||
getLoaderName() {
|
||||
return 'test-loader';
|
||||
},
|
||||
async load() {
|
||||
return {
|
||||
features: [
|
||||
createFrontendPlugin({
|
||||
pluginId: 'test',
|
||||
extensions: [
|
||||
PageBlueprint.make({
|
||||
params: {
|
||||
defaultPath: '/',
|
||||
loader: () => new Promise(() => {}),
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const { features } = await resolveAsyncFeatures({
|
||||
config: mockApis.config(),
|
||||
features: [loader],
|
||||
});
|
||||
|
||||
expect(features).toMatchObject([
|
||||
{
|
||||
$$type: '@backstage/FrontendPlugin',
|
||||
id: 'test',
|
||||
version: 'v1',
|
||||
extensions: [
|
||||
{
|
||||
$$type: '@backstage/Extension',
|
||||
id: 'page:test',
|
||||
version: 'v2',
|
||||
attachTo: {
|
||||
id: 'app/routes',
|
||||
input: 'routes',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should propagate errors thrown by deprecated feature loaders', async () => {
|
||||
const loader: CreateAppFeatureLoader = {
|
||||
getLoaderName() {
|
||||
return 'test-loader';
|
||||
},
|
||||
async load() {
|
||||
throw new TypeError('boom');
|
||||
},
|
||||
};
|
||||
|
||||
await expect(() =>
|
||||
resolveAsyncFeatures({
|
||||
config: mockApis.config(),
|
||||
features: [loader],
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Failed to read frontend features from loader 'test-loader', TypeError: boom"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('supports feature loaders', async () => {
|
||||
const loader: FrontendFeatureLoader = createFrontendFeatureLoader({
|
||||
async loader({ config: _ }) {
|
||||
|
||||
@@ -20,40 +20,14 @@ import {
|
||||
FrontendFeature,
|
||||
FrontendFeatureLoader,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { CreateAppFeatureLoader } from './createApp';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { isInternalFrontendFeatureLoader } from '../../frontend-plugin-api/src/wiring/createFrontendFeatureLoader';
|
||||
|
||||
/** @public */
|
||||
export async function resolveAsyncFeatures(options: {
|
||||
config: Config;
|
||||
features?: (
|
||||
| FrontendFeature
|
||||
| FrontendFeatureLoader
|
||||
| CreateAppFeatureLoader
|
||||
)[];
|
||||
features?: (FrontendFeature | FrontendFeatureLoader)[];
|
||||
}): Promise<{ features: FrontendFeature[] }> {
|
||||
const features: (FrontendFeature | FrontendFeatureLoader)[] = [];
|
||||
|
||||
// Separate deprecated CreateAppFeatureLoader elements from the frontend features,
|
||||
// and manage the deprecated elements first.
|
||||
for (const item of options?.features ?? []) {
|
||||
if ('load' in item) {
|
||||
try {
|
||||
const result = await item.load({ config: options.config });
|
||||
features.push(...result.features);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to read frontend features from loader '${item.getLoaderName()}', ${stringifyError(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
features.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
const loadedFeatures: FrontendFeature[] = [];
|
||||
const alreadyMetFeatureLoaders: FrontendFeatureLoader[] = [];
|
||||
const maxRecursionDepth = 5;
|
||||
@@ -96,7 +70,7 @@ export async function resolveAsyncFeatures(options: {
|
||||
}
|
||||
}
|
||||
|
||||
await applyFeatureLoaders(features, 1);
|
||||
await applyFeatureLoaders(options.features ?? [], 1);
|
||||
|
||||
return { features: loadedFeatures };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user