From 8e21c4d9931759ba188ab9abb6c27a411ad373d1 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 7 Aug 2025 12:29:58 +0200 Subject: [PATCH] refactor(frontend-plugin,app-api): make app spec plugin required Signed-off-by: Camila Belo --- .changeset/fluffy-otters-cry.md | 5 +++ .changeset/thirty-eagles-run.md | 5 +++ .../src/tree/instantiateAppNodeTree.test.ts | 3 +- .../src/tree/resolveAppNodeSpecs.test.ts | 43 ++++++++++++++++++- .../src/tree/resolveAppNodeSpecs.ts | 12 ++++-- .../src/tree/resolveAppTree.test.ts | 42 +++++++++++++++--- .../src/apis/definitions/AppTreeApi.ts | 2 +- 7 files changed, 100 insertions(+), 12 deletions(-) create mode 100644 .changeset/fluffy-otters-cry.md create mode 100644 .changeset/thirty-eagles-run.md diff --git a/.changeset/fluffy-otters-cry.md b/.changeset/fluffy-otters-cry.md new file mode 100644 index 0000000000..2798473c2f --- /dev/null +++ b/.changeset/fluffy-otters-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': minor +--- + +Use an empty root plugin for built-in extension app node specs. diff --git a/.changeset/thirty-eagles-run.md b/.changeset/thirty-eagles-run.md new file mode 100644 index 0000000000..af2809e3f4 --- /dev/null +++ b/.changeset/thirty-eagles-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': minor +--- + +**BREAKING:** The `AppNodeSpec.plugin` property is now required. diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index f3201e97b2..794a2a3671 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -24,6 +24,7 @@ import { createExtension, createExtensionDataRef, createExtensionInput, + createFrontendPlugin, } from '@backstage/frontend-plugin-api'; import { createAppNodeInstance, @@ -56,7 +57,7 @@ function makeSpec( attachTo: extension.attachTo, disabled: extension.disabled, extension: extension as Extension, - plugin: undefined, + plugin: createFrontendPlugin({ pluginId: 'root' }), ...spec, }; } diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts index 979c851a47..276f47c9d8 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -20,7 +20,7 @@ import { Extension, ExtensionDefinition, } from '@backstage/frontend-plugin-api'; -import { resolveAppNodeSpecs } from './resolveAppNodeSpecs'; +import { resolveAppNodeSpecs, rootPlugin } from './resolveAppNodeSpecs'; function makeExt( id: string, @@ -62,12 +62,15 @@ describe('resolveAppNodeSpecs', () => { builtinExtensions: [a], parameters: [], }), - ).toEqual([ + ).toStrictEqual([ { id: 'a', extension: a, attachTo: { id: 'root', input: 'default' }, disabled: true, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, ]); }); @@ -87,12 +90,18 @@ describe('resolveAppNodeSpecs', () => { extension: a, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'b', extension: b, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, ]); }); @@ -120,6 +129,9 @@ describe('resolveAppNodeSpecs', () => { extension: b, attachTo: { id: 'derp', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'test/a', @@ -204,12 +216,18 @@ describe('resolveAppNodeSpecs', () => { extension: b, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'a', extension: a, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, ]); }); @@ -238,42 +256,63 @@ describe('resolveAppNodeSpecs', () => { extension: e, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'd', extension: d, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'c', extension: c, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'a', extension: a, attachTo: { id: 'root', input: 'default' }, disabled: true, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'b', extension: b, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'f', extension: f, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, { id: 'g', extension: g, attachTo: { id: 'root', input: 'default' }, disabled: true, + config: undefined, + plugin: rootPlugin, + source: rootPlugin, }, ]); }); diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 4788734800..46a6042729 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Extension, FrontendFeature } from '@backstage/frontend-plugin-api'; +import { + createFrontendPlugin, + Extension, + FrontendFeature, +} from '@backstage/frontend-plugin-api'; import { ExtensionParameters } from './readAppExtensionsConfig'; import { AppNodeSpec } from '@backstage/frontend-plugin-api'; import { OpaqueFrontendPlugin } from '@internal/frontend'; @@ -26,6 +30,8 @@ import { // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; +export const rootPlugin = createFrontendPlugin({ pluginId: 'root' }); + /** @internal */ export function resolveAppNodeSpecs(options: { features?: FrontendFeature[]; @@ -106,8 +112,8 @@ export function resolveAppNodeSpecs(options: { return { extension: internalExtension, params: { - source: undefined, - plugin: undefined, + source: rootPlugin, + plugin: rootPlugin, attachTo: internalExtension.attachTo, disabled: internalExtension.disabled, config: undefined as unknown, diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts index e93d311f8d..3efd500a78 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts @@ -18,6 +18,7 @@ import { coreExtensionData, createExtension, createExtensionInput, + createFrontendPlugin, Extension, } from '@backstage/frontend-plugin-api'; import { resolveAppTree } from './resolveAppTree'; @@ -37,6 +38,7 @@ const baseSpec = { extension, attachTo: { id: 'nonexistent', input: 'nonexistent' }, disabled: false, + plugin: createFrontendPlugin({ pluginId: 'root' }), }; describe('buildAppTree', () => { @@ -284,8 +286,20 @@ describe('buildAppTree', () => { ) as Extension; const tree = resolveAppTree('a', [ - { attachTo: e1.attachTo, id: 'a', extension: e1, disabled: false }, - { attachTo: e2.attachTo, id: 'b', extension: e2, disabled: false }, + { + attachTo: e1.attachTo, + id: 'a', + extension: e1, + disabled: false, + plugin: baseSpec.plugin, + }, + { + attachTo: e2.attachTo, + id: 'b', + extension: e2, + disabled: false, + plugin: baseSpec.plugin, + }, ]); expect(tree.root).toMatchInlineSnapshot(` @@ -352,9 +366,27 @@ describe('buildAppTree', () => { ) as Extension; const tree = resolveAppTree('test-2', [ - { attachTo: e1.attachTo, id: e1.id, extension: e1, disabled: false }, - { attachTo: e2.attachTo, id: e2.id, extension: e2, disabled: false }, - { attachTo: e3.attachTo, id: e3.id, extension: e3, disabled: false }, + { + attachTo: e1.attachTo, + id: e1.id, + extension: e1, + disabled: false, + plugin: baseSpec.plugin, + }, + { + attachTo: e2.attachTo, + id: e2.id, + extension: e2, + disabled: false, + plugin: baseSpec.plugin, + }, + { + attachTo: e3.attachTo, + id: e3.id, + extension: e3, + disabled: false, + plugin: baseSpec.plugin, + }, ]); expect(tree.nodes.get('test-3')?.edges.attachedTo?.node).toBe( diff --git a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts index c205790aae..1a4752a81f 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts @@ -37,7 +37,7 @@ export interface AppNodeSpec { readonly extension: Extension; readonly disabled: boolean; readonly config?: unknown; - readonly plugin?: FrontendPlugin; + readonly plugin: FrontendPlugin; } /**