From 44735df63341380ee27da62ccf154b7025254c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 14 Dec 2023 10:33:44 +0100 Subject: [PATCH] no more featureLoader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/fluffy-bikes-laugh.md | 5 +++ packages/frontend-app-api/api-report.md | 8 +++- packages/frontend-app-api/package.json | 1 + .../src/wiring/createApp.test.tsx | 27 +++++++++++++ .../frontend-app-api/src/wiring/createApp.tsx | 39 +++++++++++++++---- packages/frontend-app-api/src/wiring/index.ts | 1 + yarn.lock | 1 + 7 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 .changeset/fluffy-bikes-laugh.md diff --git a/.changeset/fluffy-bikes-laugh.md b/.changeset/fluffy-bikes-laugh.md new file mode 100644 index 0000000000..40be65c126 --- /dev/null +++ b/.changeset/fluffy-bikes-laugh.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': minor +--- + +Removed `featureLoader` from `createApp`, `features` instead accepts both `FrontendFeature` and `CreateAppFeatureLoader` diff --git a/packages/frontend-app-api/api-report.md b/packages/frontend-app-api/api-report.md index a4654e9f8d..08db759511 100644 --- a/packages/frontend-app-api/api-report.md +++ b/packages/frontend-app-api/api-report.md @@ -14,16 +14,20 @@ import { SubRouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) export function createApp(options?: { - features?: FrontendFeature[]; + features?: (FrontendFeature | CreateAppFeatureLoader)[]; configLoader?: () => Promise<{ config: ConfigApi; }>; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; - featureLoader?: (ctx: { config: ConfigApi }) => Promise; }): { createRoot(): JSX_2.Element; }; +// @public +export type CreateAppFeatureLoader = (options: { + config: ConfigApi; +}) => Promise; + // @public export type CreateAppRouteBinder = < TExternalRoutes extends { diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 11f7260a20..db4dc931f2 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -38,6 +38,7 @@ "@backstage/core-app-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/theme": "workspace:^", "@backstage/types": "workspace:^", diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index dafad5365c..a7956137c4 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -99,6 +99,33 @@ describe('createApp', () => { ); }); + it('should support feature loaders', async () => { + const app = createApp({ + configLoader: async () => ({ + config: new MockConfigApi({ key: 'config-value' }), + }), + features: [ + async ({ config }) => [ + createPlugin({ + id: 'test', + extensions: [ + createPageExtension({ + defaultPath: '/', + loader: async () =>
{config.getString('key')}
, + }), + ], + }), + ], + ], + }); + + await renderWithEffects(app.createRoot()); + + await expect( + screen.findByText('config-value'), + ).resolves.toBeInTheDocument(); + }); + it('should register feature flags', async () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 0f8e3de953..f3b32db399 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -101,6 +101,7 @@ import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiri // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; import { DefaultComponentsApi } from '../apis/implementations/ComponentsApi'; +import { stringifyError } from '@backstage/errors'; export const builtinExtensions = [ Core, @@ -238,12 +239,20 @@ function deduplicateFeatures( .reverse(); } +/** + * A source of dynamically loaded frontend features. + * + * @public + */ +export type CreateAppFeatureLoader = (options: { + config: ConfigApi; +}) => Promise; + /** @public */ export function createApp(options?: { - features?: FrontendFeature[]; + features?: (FrontendFeature | CreateAppFeatureLoader)[]; configLoader?: () => Promise<{ config: ConfigApi }>; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; - featureLoader?: (ctx: { config: ConfigApi }) => Promise; }): { createRoot(): JSX.Element; } { @@ -255,15 +264,28 @@ export function createApp(options?: { ); const discoveredFeatures = getAvailableFeatures(config); - const loadedFeatures = (await options?.featureLoader?.({ config })) ?? []; + + const providedFeatures: FrontendFeature[] = []; + for (const feature of options?.features ?? []) { + if (typeof feature === 'function') { + try { + const loadedFeatures = await feature({ config }); + providedFeatures.push(...loadedFeatures); + } catch (e) { + throw new Error( + `Failed to read frontend features from loader, ${stringifyError( + e, + )}`, + ); + } + } else { + providedFeatures.push(feature); + } + } const app = createSpecializedApp({ config, - features: [ - ...discoveredFeatures, - ...loadedFeatures, - ...(options?.features ?? []), - ], + features: [...discoveredFeatures, ...providedFeatures], bindRoutes: options?.bindRoutes, }).createRoot(); @@ -285,6 +307,7 @@ export function createApp(options?: { /** * Synchronous version of {@link createApp}, expecting all features and * config to have been loaded already. + * * @public */ export function createSpecializedApp(options?: { diff --git a/packages/frontend-app-api/src/wiring/index.ts b/packages/frontend-app-api/src/wiring/index.ts index 412e643523..00c6458bd5 100644 --- a/packages/frontend-app-api/src/wiring/index.ts +++ b/packages/frontend-app-api/src/wiring/index.ts @@ -18,6 +18,7 @@ export { createApp, createSpecializedApp, createExtensionTree, + type CreateAppFeatureLoader, type ExtensionTreeNode, type ExtensionTree, } from './createApp'; diff --git a/yarn.lock b/yarn.lock index 8a7e8a1dac..22ca9ef371 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4158,6 +4158,7 @@ __metadata: "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^"