From 24985f51cea46e58fd08a96118c644aaa7bdc3a8 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Sep 2023 13:59:15 +0200 Subject: [PATCH 1/4] frontend-app-api: Add Extension utilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Fredrik Adelöw Co-authored-by: Camila Belo Signed-off-by: Johan Haals --- packages/frontend-app-api/api-report.md | 33 ++++++++ .../frontend-app-api/src/wiring/createApp.tsx | 78 +++++++++++++++---- .../src/wiring/createExtensionInstance.ts | 2 +- packages/frontend-app-api/src/wiring/index.ts | 8 +- 4 files changed, 106 insertions(+), 15 deletions(-) diff --git a/packages/frontend-app-api/api-report.md b/packages/frontend-app-api/api-report.md index 5c003e8129..fe0ceecbc6 100644 --- a/packages/frontend-app-api/api-report.md +++ b/packages/frontend-app-api/api-report.md @@ -5,7 +5,11 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigApi } from '@backstage/core-plugin-api'; +<<<<<<< HEAD import { JSX as JSX_2 } from 'react'; +======= +import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +>>>>>>> ed3fd70bda19 (frontend-app-api: Add Extension utilities) // @public (undocumented) export function createApp(options: { @@ -14,4 +18,33 @@ export function createApp(options: { }): { createRoot(): JSX_2.Element; }; + +// @public (undocumented) +export function createExtensionTree(): ExtensionTree; + +// @public (undocumented) +export interface ExtensionInstance { + // (undocumented) + readonly $$type: '@backstage/ExtensionInstance'; + readonly attachments: Map; + getData(ref: ExtensionDataRef): T | undefined; + // (undocumented) + readonly id: string; +} + +// @public (undocumented) +export interface ExtensionTree { + // (undocumented) + getExtension(id: string): ExtensionInstance | undefined; + // (undocumented) + getExtensionAttachments(id: string, inputName: string): ExtensionInstance[]; +} + +// @public (undocumented) +export interface ExtensionTreeNode { + // (undocumented) + getData(ref: ExtensionDataRef): T | undefined; + // (undocumented) + id: string; +} ``` diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index c65c511e55..e5dc1db847 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -19,6 +19,7 @@ import { ConfigReader } from '@backstage/config'; import { BackstagePlugin, coreExtensionData, + ExtensionDataRef, } from '@backstage/frontend-plugin-api'; import { Core } from '../extensions/Core'; import { CoreRoutes } from '../extensions/CoreRoutes'; @@ -76,26 +77,55 @@ import { import { BrowserRouter } from 'react-router-dom'; /** @public */ -export function createApp(options: { - plugins: BackstagePlugin[]; - config?: ConfigApi; -}): { - createRoot(): JSX.Element; -} { - const appConfig = - options?.config ?? - ConfigReader.fromConfigs(overrideBaseUrlConfigs(defaultConfigLoaderSync())); +export interface ExtensionTreeNode { + id: string; + getData(ref: ExtensionDataRef): T | undefined; +} +/** @public */ +export interface ExtensionTree { + getExtension(id: string): ExtensionInstance | undefined; + getExtensionAttachments(id: string, inputName: string): ExtensionInstance[]; +} + +/** @public */ +export function createExtensionTree(): ExtensionTree { + const plugins = getAvailablePlugins(); + const { instances } = createInstances({ + plugins, + config: ConfigReader.fromConfigs( + overrideBaseUrlConfigs(defaultConfigLoaderSync()), + ), + }); + + return { + getExtension(id: string): ExtensionInstance | undefined { + return instances.get(id); + }, + getExtensionAttachments( + id: string, + inputName: string, + ): ExtensionInstance[] { + return this.getExtension(id)?.attachments.get(inputName) ?? []; + }, + }; +} + +/** + * @internal + */ +export function createInstances(options: { + plugins: BackstagePlugin[]; + config: ConfigApi; +}) { const builtinExtensions = [Core, CoreRoutes, CoreNav, CoreLayout]; - 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: allPlugins, + sources: options.plugins, builtinExtensions, - parameters: readAppExtensionParameters(appConfig), + parameters: readAppExtensionParameters(options.config), }); // TODO: validate the config of all extension instances @@ -156,10 +186,32 @@ export function createApp(options: { } const rootConfigs = attachmentMap.get('root')?.get('default') ?? []; + const rootInstances = rootConfigs.map(instanceParams => createInstance(instanceParams), ); + return { instances, rootInstances }; +} + +/** @public */ +export function createApp(options: { + plugins: BackstagePlugin[]; + config?: ConfigApi; +}): { + createRoot(): JSX.Element; +} { + const discoveredPlugins = getAvailablePlugins(); + const allPlugins = [...discoveredPlugins, ...options.plugins]; + const appConfig = + options?.config ?? + ConfigReader.fromConfigs(overrideBaseUrlConfigs(defaultConfigLoaderSync())); + + const { rootInstances } = createInstances({ + plugins: allPlugins, + config: appConfig, + }); + const routePaths = extractRouteInfoFromInstanceTree(rootInstances); const coreInstance = rootInstances.find(({ id }) => id === 'core'); diff --git a/packages/frontend-app-api/src/wiring/createExtensionInstance.ts b/packages/frontend-app-api/src/wiring/createExtensionInstance.ts index 52afe95121..0823f4e6e0 100644 --- a/packages/frontend-app-api/src/wiring/createExtensionInstance.ts +++ b/packages/frontend-app-api/src/wiring/createExtensionInstance.ts @@ -21,7 +21,7 @@ import { } from '@backstage/frontend-plugin-api'; import mapValues from 'lodash/mapValues'; -/** @internal */ +/** @public */ export interface ExtensionInstance { readonly $$type: '@backstage/ExtensionInstance'; diff --git a/packages/frontend-app-api/src/wiring/index.ts b/packages/frontend-app-api/src/wiring/index.ts index a43cd73df2..7eb1cc2de7 100644 --- a/packages/frontend-app-api/src/wiring/index.ts +++ b/packages/frontend-app-api/src/wiring/index.ts @@ -14,4 +14,10 @@ * limitations under the License. */ -export { createApp } from './createApp'; +export type { ExtensionInstance } from './createExtensionInstance'; +export { + createApp, + createExtensionTree, + type ExtensionTreeNode, + type ExtensionTree, +} from './createApp'; From d137cf53be7efccfe6c5df17f36e4b59f6722787 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Sep 2023 15:16:14 +0200 Subject: [PATCH 2/4] frontend-app-api: Return ExtensionTreeNode instead of raw instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Camila Belo Signed-off-by: Johan Haals --- packages/frontend-app-api/api-report.md | 17 ++++---------- .../frontend-app-api/src/wiring/createApp.tsx | 22 +++++++++---------- packages/frontend-app-api/src/wiring/index.ts | 1 - 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/packages/frontend-app-api/api-report.md b/packages/frontend-app-api/api-report.md index fe0ceecbc6..0c6471480d 100644 --- a/packages/frontend-app-api/api-report.md +++ b/packages/frontend-app-api/api-report.md @@ -4,6 +4,7 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; +import { Config } from '@backstage/config'; import { ConfigApi } from '@backstage/core-plugin-api'; <<<<<<< HEAD import { JSX as JSX_2 } from 'react'; @@ -20,24 +21,14 @@ export function createApp(options: { }; // @public (undocumented) -export function createExtensionTree(): ExtensionTree; - -// @public (undocumented) -export interface ExtensionInstance { - // (undocumented) - readonly $$type: '@backstage/ExtensionInstance'; - readonly attachments: Map; - getData(ref: ExtensionDataRef): T | undefined; - // (undocumented) - readonly id: string; -} +export function createExtensionTree(options: { config: Config }): ExtensionTree; // @public (undocumented) export interface ExtensionTree { // (undocumented) - getExtension(id: string): ExtensionInstance | undefined; + getExtension(id: string): ExtensionTreeNode | undefined; // (undocumented) - getExtensionAttachments(id: string, inputName: string): ExtensionInstance[]; + getExtensionAttachments(id: string, inputName: string): ExtensionTreeNode[]; } // @public (undocumented) diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index e5dc1db847..23b7b4ca11 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -15,7 +15,7 @@ */ import React, { JSX } from 'react'; -import { ConfigReader } from '@backstage/config'; +import { ConfigReader, Config } from '@backstage/config'; import { BackstagePlugin, coreExtensionData, @@ -84,29 +84,29 @@ export interface ExtensionTreeNode { /** @public */ export interface ExtensionTree { - getExtension(id: string): ExtensionInstance | undefined; - getExtensionAttachments(id: string, inputName: string): ExtensionInstance[]; + getExtension(id: string): ExtensionTreeNode | undefined; + getExtensionAttachments(id: string, inputName: string): ExtensionTreeNode[]; } /** @public */ -export function createExtensionTree(): ExtensionTree { +export function createExtensionTree(options: { + config: Config; +}): ExtensionTree { const plugins = getAvailablePlugins(); const { instances } = createInstances({ plugins, - config: ConfigReader.fromConfigs( - overrideBaseUrlConfigs(defaultConfigLoaderSync()), - ), + config: options.config, }); return { - getExtension(id: string): ExtensionInstance | undefined { + getExtension(id: string): ExtensionTreeNode | undefined { return instances.get(id); }, getExtensionAttachments( id: string, inputName: string, - ): ExtensionInstance[] { - return this.getExtension(id)?.attachments.get(inputName) ?? []; + ): ExtensionTreeNode[] { + return instances.get(id)?.attachments.get(inputName) ?? []; }, }; } @@ -116,7 +116,7 @@ export function createExtensionTree(): ExtensionTree { */ export function createInstances(options: { plugins: BackstagePlugin[]; - config: ConfigApi; + config: Config; }) { const builtinExtensions = [Core, CoreRoutes, CoreNav, CoreLayout]; diff --git a/packages/frontend-app-api/src/wiring/index.ts b/packages/frontend-app-api/src/wiring/index.ts index 7eb1cc2de7..dc1ba3bc13 100644 --- a/packages/frontend-app-api/src/wiring/index.ts +++ b/packages/frontend-app-api/src/wiring/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -export type { ExtensionInstance } from './createExtensionInstance'; export { createApp, createExtensionTree, From e753c3f27b1b8d5bc5fb0b71f2c37d1bb8611321 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 13 Sep 2023 14:25:02 +0200 Subject: [PATCH 3/4] Mark ExtensionInstance as internal Signed-off-by: Johan Haals --- packages/frontend-app-api/src/wiring/createExtensionInstance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-app-api/src/wiring/createExtensionInstance.ts b/packages/frontend-app-api/src/wiring/createExtensionInstance.ts index 0823f4e6e0..52afe95121 100644 --- a/packages/frontend-app-api/src/wiring/createExtensionInstance.ts +++ b/packages/frontend-app-api/src/wiring/createExtensionInstance.ts @@ -21,7 +21,7 @@ import { } from '@backstage/frontend-plugin-api'; import mapValues from 'lodash/mapValues'; -/** @public */ +/** @internal */ export interface ExtensionInstance { readonly $$type: '@backstage/ExtensionInstance'; From 6b131584717c9e3b6ac7e13d89e2140194381292 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 13 Sep 2023 14:26:41 +0200 Subject: [PATCH 4/4] Fix api report Signed-off-by: Johan Haals --- packages/frontend-app-api/api-report.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/frontend-app-api/api-report.md b/packages/frontend-app-api/api-report.md index 0c6471480d..04f30c2616 100644 --- a/packages/frontend-app-api/api-report.md +++ b/packages/frontend-app-api/api-report.md @@ -6,11 +6,8 @@ import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { Config } from '@backstage/config'; import { ConfigApi } from '@backstage/core-plugin-api'; -<<<<<<< HEAD -import { JSX as JSX_2 } from 'react'; -======= import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; ->>>>>>> ed3fd70bda19 (frontend-app-api: Add Extension utilities) +import { JSX as JSX_2 } from 'react'; // @public (undocumented) export function createApp(options: {