diff --git a/.changeset/fluffy-otters-cry.md b/.changeset/fluffy-otters-cry.md new file mode 100644 index 0000000000..fd24a0bb47 --- /dev/null +++ b/.changeset/fluffy-otters-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': minor +--- + +Use an app 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..10ca1af381 --- /dev/null +++ b/.changeset/thirty-eagles-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': minor +--- + +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 5fa22536f8..57ec7b23f3 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -27,6 +27,7 @@ import { createExtensionBlueprint, createExtensionDataRef, createExtensionInput, + createFrontendPlugin, } from '@backstage/frontend-plugin-api'; import { createAppNodeInstance, @@ -59,7 +60,7 @@ function makeSpec( attachTo: extension.attachTo, disabled: extension.disabled, extension: extension as Extension, - plugin: undefined, + plugin: createFrontendPlugin({ pluginId: 'app' }), ...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..83abcf3dc4 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -68,6 +68,9 @@ describe('resolveAppNodeSpecs', () => { extension: a, attachTo: { id: 'root', input: 'default' }, disabled: true, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, ]); }); @@ -87,12 +90,18 @@ describe('resolveAppNodeSpecs', () => { extension: a, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'b', extension: b, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, ]); }); @@ -120,6 +129,9 @@ describe('resolveAppNodeSpecs', () => { extension: b, attachTo: { id: 'derp', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'test/a', @@ -204,12 +216,18 @@ describe('resolveAppNodeSpecs', () => { extension: b, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'a', extension: a, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, ]); }); @@ -238,42 +256,63 @@ describe('resolveAppNodeSpecs', () => { extension: e, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'd', extension: d, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'c', extension: c, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'a', extension: a, attachTo: { id: 'root', input: 'default' }, disabled: true, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'b', extension: b, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'f', extension: f, attachTo: { id: 'root', input: 'default' }, disabled: false, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, { id: 'g', extension: g, attachTo: { id: 'root', input: 'default' }, disabled: true, + config: undefined, + plugin: expect.any(Object), + source: expect.any(Object), }, ]); }); diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 4788734800..f9ce7387c2 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'; @@ -87,6 +91,12 @@ export function resolveAppNodeSpecs(options: { ); } + const appPlugin = + plugins.find(plugin => plugin.id === 'app') ?? + createFrontendPlugin({ + pluginId: 'app', + }); + const configuredExtensions = [ ...pluginExtensions.map(({ plugin, ...extension }) => { const internalExtension = toInternalExtension(extension); @@ -106,8 +116,8 @@ export function resolveAppNodeSpecs(options: { return { extension: internalExtension, params: { - source: undefined, - plugin: undefined, + source: appPlugin, + plugin: appPlugin, 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..f1161b202e 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: 'app' }), }; 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/report.api.md b/packages/frontend-plugin-api/report.api.md index 78c2ba361f..469733987e 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -268,7 +268,7 @@ export interface AppNodeSpec { // (undocumented) readonly id: string; // (undocumented) - readonly plugin?: FrontendPlugin; + readonly plugin: FrontendPlugin; } // @public diff --git a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts index 3ce55e1525..f4fd2f3a6f 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; } /**