From e0088d9d1a4617295cbbaa94a36630318a3e7194 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 8 Sep 2023 11:17:42 +0200 Subject: [PATCH] frontend-app-api: implement api factory discovery Co-authored-by: Camila Belo Signed-off-by: Patrik Oldsberg --- packages/frontend-app-api/package.json | 1 + packages/frontend-app-api/src/createApp.tsx | 66 +++++++++++++++---- .../frontend-app-api/src/extensions/Core.tsx | 34 ++++++++++ yarn.lock | 1 + 4 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 packages/frontend-app-api/src/extensions/Core.tsx diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 33b6067858..17f4b1bef3 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -34,6 +34,7 @@ ], "dependencies": { "@backstage/config": "workspace:^", + "@backstage/core-app-api": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-graphiql": "workspace:^", diff --git a/packages/frontend-app-api/src/createApp.tsx b/packages/frontend-app-api/src/createApp.tsx index c50b507642..77f0d1948a 100644 --- a/packages/frontend-app-api/src/createApp.tsx +++ b/packages/frontend-app-api/src/createApp.tsx @@ -21,6 +21,7 @@ import { coreExtensionData, } from '@backstage/frontend-plugin-api'; import { CoreRouter } from './extensions/CoreRouter'; +import { Core } from './extensions/Core'; import { createExtensionInstance, ExtensionInstance, @@ -31,8 +32,13 @@ import { readAppExtensionParameters, } from './wiring/parameters'; import { RoutingProvider } from './routing/RoutingContext'; -import { RouteRef } from '@backstage/core-plugin-api'; +import { ApiHolder, RouteRef } from '@backstage/core-plugin-api'; import { getAvailablePlugins } from './wiring/discovery'; +import { + ApiFactoryRegistry, + ApiProvider, + ApiResolver, +} from '@backstage/core-app-api'; /** @public */ export function createApp(options: { plugins: BackstagePlugin[] }): { @@ -40,7 +46,7 @@ export function createApp(options: { plugins: BackstagePlugin[] }): { } { const appConfig = ConfigReader.fromConfigs(process.env.APP_CONFIG as any); - const builtinExtensions = [CoreRouter]; + const builtinExtensions = [CoreRouter, Core]; const discoveredPlugins = getAvailablePlugins(); // pull in default extension instance from discovered packages @@ -115,25 +121,59 @@ export function createApp(options: { plugins: BackstagePlugin[] }): { const routePaths = extractRouteInfoFromInstanceTree(rootInstances); + const coreInstance = rootInstances.find(({ id }) => id === 'core'); + if (!coreInstance) { + throw Error('Unable to find core extension instance'); + } + + const apiHolder = createApiHolder(coreInstance); + return { createRoot() { - const rootComponents = rootInstances.map( - e => - e.data.get( - coreExtensionData.reactComponent.id, - ) as typeof coreExtensionData.reactComponent.T, - ); + const rootComponents = rootInstances + .map( + e => + e.data.get( + coreExtensionData.reactComponent.id, + ) as typeof coreExtensionData.reactComponent.T, + ) + .filter(Boolean); return ( - - {rootComponents.map((Component, i) => ( - - ))} - + + + {rootComponents.map((Component, i) => ( + + ))} + + ); }, }; } +function createApiHolder(coreExtension: ExtensionInstance): ApiHolder { + const factoryRegistry = new ApiFactoryRegistry(); + + const apiFactories = + coreExtension.attachments + .get('apis') + ?.map( + e => + e.data.get( + coreExtensionData.apiFactory.id, + ) as typeof coreExtensionData.apiFactory.T, + ) + .filter(Boolean) ?? []; + + for (const factory of apiFactories) { + factoryRegistry.register('default', factory); + } + + ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis()); + + return new ApiResolver(factoryRegistry); +} + /** @internal */ export function extractRouteInfoFromInstanceTree( roots: ExtensionInstance[], diff --git a/packages/frontend-app-api/src/extensions/Core.tsx b/packages/frontend-app-api/src/extensions/Core.tsx new file mode 100644 index 0000000000..cb93b2888f --- /dev/null +++ b/packages/frontend-app-api/src/extensions/Core.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + coreExtensionData, + createExtension, +} from '@backstage/frontend-plugin-api'; + +export const Core = createExtension({ + id: 'core', + at: 'root', + inputs: { + apis: { + extensionData: { + api: coreExtensionData.apiFactory, + }, + }, + }, + output: {}, + factory() {}, +}); diff --git a/yarn.lock b/yarn.lock index 7075f315dc..4a9b30d532 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4317,6 +4317,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/core-app-api": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-graphiql": "workspace:^"