diff --git a/.changeset/eight-suns-tan.md b/.changeset/eight-suns-tan.md new file mode 100644 index 0000000000..4b8634a9e1 --- /dev/null +++ b/.changeset/eight-suns-tan.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +Extension attachment point is now configured via `attachTo: { id, input }` instead of `at: 'id/input'`. diff --git a/.changeset/plenty-toys-cheer.md b/.changeset/plenty-toys-cheer.md new file mode 100644 index 0000000000..3c16002632 --- /dev/null +++ b/.changeset/plenty-toys-cheer.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-react': patch +'@backstage/plugin-graphiql': patch +--- + +Updated `/alpha` exports to use new `attachTo` option. diff --git a/.changeset/small-books-deliver.md b/.changeset/small-books-deliver.md new file mode 100644 index 0000000000..0d09f7ae1c --- /dev/null +++ b/.changeset/small-books-deliver.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Updates for `at` -> `attachTo` refactor. diff --git a/packages/frontend-app-api/src/extensions/Core.tsx b/packages/frontend-app-api/src/extensions/Core.tsx index 4cb21a8702..4a66afe38d 100644 --- a/packages/frontend-app-api/src/extensions/Core.tsx +++ b/packages/frontend-app-api/src/extensions/Core.tsx @@ -22,7 +22,7 @@ import { export const Core = createExtension({ id: 'core', - at: 'root', + attachTo: { id: 'root', input: 'default' }, inputs: { apis: createExtensionInput({ api: coreExtensionData.apiFactory, diff --git a/packages/frontend-app-api/src/extensions/CoreLayout.tsx b/packages/frontend-app-api/src/extensions/CoreLayout.tsx index 0c5c0446fc..9b0b8b25d1 100644 --- a/packages/frontend-app-api/src/extensions/CoreLayout.tsx +++ b/packages/frontend-app-api/src/extensions/CoreLayout.tsx @@ -24,7 +24,7 @@ import { SidebarPage } from '@backstage/core-components'; export const CoreLayout = createExtension({ id: 'core.layout', - at: 'root', + attachTo: { id: 'root', input: 'default' }, inputs: { nav: createExtensionInput( { diff --git a/packages/frontend-app-api/src/extensions/CoreNav.tsx b/packages/frontend-app-api/src/extensions/CoreNav.tsx index 71ab71a6aa..8e8aa638c0 100644 --- a/packages/frontend-app-api/src/extensions/CoreNav.tsx +++ b/packages/frontend-app-api/src/extensions/CoreNav.tsx @@ -73,7 +73,7 @@ const SidebarNavItem = (props: NavTarget) => { export const CoreNav = createExtension({ id: 'core.nav', - at: 'core.layout/nav', + attachTo: { id: 'core.layout', input: 'nav' }, inputs: { items: createExtensionInput({ target: coreExtensionData.navTarget, diff --git a/packages/frontend-app-api/src/extensions/CoreRoutes.tsx b/packages/frontend-app-api/src/extensions/CoreRoutes.tsx index 5c79b56f43..5006a87cac 100644 --- a/packages/frontend-app-api/src/extensions/CoreRoutes.tsx +++ b/packages/frontend-app-api/src/extensions/CoreRoutes.tsx @@ -24,7 +24,7 @@ import { useRoutes } from 'react-router-dom'; export const CoreRoutes = createExtension({ id: 'core.routes', - at: 'core.layout/content', + attachTo: { id: 'core.layout', input: 'content' }, inputs: { routes: createExtensionInput({ path: coreExtensionData.routePath, diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts index c64dc7a3b9..a510adf1b7 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts @@ -40,13 +40,15 @@ const refOrder = [ref1, ref2, ref3, ref4, ref5]; function createTestExtension(options: { id: string; - at?: string; + parent?: string; path?: string; routeRef?: RouteRef; }) { return createExtension({ id: options.id, - at: options.at ?? 'core.routes/children', + attachTo: options.parent + ? { id: options.parent, input: 'children' } + : { id: 'core.routes', input: 'children' }, output: { element: coreExtensionData.reactElement, path: coreExtensionData.routePath.optional(), @@ -126,13 +128,13 @@ describe('discovery', () => { }), createTestExtension({ id: 'page2', - at: 'page1/children', + parent: 'page1', path: 'bar/:id', routeRef: ref2, }), createTestExtension({ id: 'page3', - at: 'page2/children', + parent: 'page2', path: 'baz', routeRef: ref3, }), @@ -143,7 +145,7 @@ describe('discovery', () => { }), createTestExtension({ id: 'page5', - at: 'page1/children', + parent: 'page1', path: 'blop', routeRef: ref5, }), @@ -194,7 +196,7 @@ describe('discovery', () => { }), createTestExtension({ id: 'page2', - at: 'page1/children', + parent: 'page1', path: 'bar/:id', routeRef: ref2, }), @@ -205,13 +207,13 @@ describe('discovery', () => { }), createTestExtension({ id: 'page4', - at: 'page3/children', + parent: 'page3', path: 'divsoup', routeRef: ref4, }), createTestExtension({ id: 'page5', - at: 'page3/children', + parent: 'page3', path: 'blop', routeRef: ref5, }), @@ -242,7 +244,7 @@ describe('discovery', () => { }), createTestExtension({ id: 'page2', - at: 'page1/children', + parent: 'page1', path: '/bar/:id', routeRef: ref2, }), @@ -253,13 +255,13 @@ describe('discovery', () => { }), createTestExtension({ id: 'page4', - at: 'page3/children', + parent: 'page3', path: '/divsoup', routeRef: ref4, }), createTestExtension({ id: 'page5', - at: 'page3/children', + parent: 'page3', path: '/blop', routeRef: ref5, }), @@ -289,16 +291,16 @@ describe('discovery', () => { }), createTestExtension({ id: 'page1', - at: 'foo/children', + parent: 'foo', routeRef: ref1, }), createTestExtension({ id: 'fooChild', - at: 'foo/children', + parent: 'foo', }), createTestExtension({ id: 'page2', - at: 'fooChild/children', + parent: 'fooChild', routeRef: ref2, }), createTestExtension({ @@ -311,17 +313,17 @@ describe('discovery', () => { }), createTestExtension({ id: 'page3Child', - at: 'page3/children', + parent: 'page3', path: '', }), createTestExtension({ id: 'page4', - at: 'page3Child/children', + parent: 'page3Child', routeRef: ref4, }), createTestExtension({ id: 'page5', - at: 'page4/children', + parent: 'page4', routeRef: ref5, }), ]); @@ -361,29 +363,29 @@ describe('discovery', () => { }), createTestExtension({ id: 'page1Child', - at: 'page1/children', + parent: 'page1', path: 'bar', }), createTestExtension({ id: 'page2', - at: 'page1Child/children', + parent: 'page1Child', routeRef: ref2, }), createTestExtension({ id: 'page3', - at: 'page2/children', + parent: 'page2', path: 'baz', routeRef: ref3, }), createTestExtension({ id: 'page4', - at: 'page3/children', + parent: 'page3', path: '/blop', routeRef: ref4, }), createTestExtension({ id: 'page5', - at: 'page2/children', + parent: 'page2', routeRef: ref5, }), ]); @@ -445,30 +447,30 @@ describe('discovery', () => { }), createTestExtension({ id: 'page1', - at: 'r/children', + parent: 'r', path: 'x', routeRef: ref1, }), createTestExtension({ id: 'y', path: 'y', - at: 'r/children', + parent: 'r', }), createTestExtension({ id: 'page2', - at: 'y/children', + parent: 'y', path: '1', routeRef: ref2, }), createTestExtension({ id: 'page3', - at: 'page2/children', + parent: 'page2', path: 'a', routeRef: ref3, }), createTestExtension({ id: 'page4', - at: 'page2/children', + parent: 'page2', path: 'b', routeRef: ref4, }), diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index f6172f5a4b..14e52af469 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -32,9 +32,7 @@ describe('createInstances', () => { app: { extensions: [ { - root: { - at: '', - }, + root: {}, }, ], }, @@ -58,7 +56,7 @@ describe('createInstances', () => { extensions: [ createExtension({ id: 'root', - at: 'core.routes/route', + attachTo: { id: 'core.routes', input: 'route' }, inputs: {}, output: {}, factory() {}, diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 540b47849f..e40c943e23 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -202,8 +202,8 @@ export function createInstances(options: { Map >(); for (const instanceParams of extensionParams) { - const [extensionId, pointId = 'default'] = instanceParams.at.split('/'); - + const extensionId = instanceParams.attachTo.id; + const pointId = instanceParams.attachTo.input; let pointMap = attachmentMap.get(extensionId); if (!pointMap) { pointMap = new Map(); diff --git a/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts b/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts index 12e109874a..6550d76608 100644 --- a/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts +++ b/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts @@ -28,7 +28,7 @@ const inputMirrorDataRef = createExtensionDataRef('mirror'); const simpleExtension = createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, output: { test: testDataRef, other: otherDataRef.optional(), @@ -101,7 +101,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, inputs: { optionalSingletonPresent: createExtensionInput( { @@ -166,7 +166,7 @@ describe('createExtensionInstance', () => { config: { other: 'not-a-number' }, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, output: {}, factory() { const error = new Error('NOPE'); @@ -188,7 +188,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, output: { test1: testDataRef, test2: testDataRef, @@ -211,7 +211,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, output: { test: testDataRef, }, @@ -232,7 +232,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, inputs: { singleton: createExtensionInput( { @@ -273,7 +273,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, inputs: { singleton: createExtensionInput( { @@ -314,7 +314,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, inputs: { singleton: createExtensionInput( { @@ -350,7 +350,7 @@ describe('createExtensionInstance', () => { config: undefined, extension: createExtension({ id: 'core.test', - at: 'ignored', + attachTo: { id: 'ignored', input: 'ignored' }, inputs: { singleton: createExtensionInput( { diff --git a/packages/frontend-app-api/src/wiring/parameters.test.ts b/packages/frontend-app-api/src/wiring/parameters.test.ts index 10b0e2fc6f..97ae941744 100644 --- a/packages/frontend-app-api/src/wiring/parameters.test.ts +++ b/packages/frontend-app-api/src/wiring/parameters.test.ts @@ -26,7 +26,7 @@ import { function makeExt(id: string, status: 'disabled' | 'enabled' = 'enabled') { return { id, - at: 'root', + attachTo: { id: 'root', input: 'default' }, disabled: status === 'disabled', } as Extension; } @@ -52,8 +52,8 @@ describe('mergeExtensionParameters', () => { parameters: [], }), ).toEqual([ - { extension: a, at: 'root' }, - { extension: b, at: 'root' }, + { extension: a, attachTo: { id: 'root', input: 'default' } }, + { extension: b, attachTo: { id: 'root', input: 'default' } }, ]); }); @@ -68,13 +68,17 @@ describe('mergeExtensionParameters', () => { parameters: [ { id: 'b', - at: 'derp', + attachTo: { id: 'derp', input: 'default' }, }, ], }), ).toEqual([ - { extension: a, at: 'root', source: pluginA }, - { extension: b, at: 'derp' }, + { + extension: a, + attachTo: { id: 'root', input: 'default' }, + source: pluginA, + }, + { extension: b, attachTo: { id: 'derp', input: 'default' } }, ]); }); @@ -102,8 +106,18 @@ describe('mergeExtensionParameters', () => { ], }), ).toEqual([ - { extension: a, at: 'root', source: plugin, config: { foo: { bar: 1 } } }, - { extension: b, at: 'root', source: plugin, config: { foo: { qux: 3 } } }, + { + extension: a, + attachTo: { id: 'root', input: 'default' }, + source: plugin, + config: { foo: { bar: 1 } }, + }, + { + extension: b, + attachTo: { id: 'root', input: 'default' }, + source: plugin, + config: { foo: { qux: 3 } }, + }, ]); }); @@ -126,8 +140,8 @@ describe('mergeExtensionParameters', () => { ], }), ).toEqual([ - { extension: b, at: 'root' }, - { extension: a, at: 'root' }, + { extension: b, attachTo: { id: 'root', input: 'default' } }, + { extension: a, attachTo: { id: 'root', input: 'default' } }, ]); }); }); @@ -315,14 +329,18 @@ describe('expandShorthandExtensionParameters', () => { expect(() => run({ 'core.router': { id: 'some.id' } }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'at', 'disabled', 'config'"`, + `"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, ); }); - it('supports object at', () => { - expect(run({ 'core.router': { at: 'other.root/inputs' } })).toEqual({ + it('supports object attachTo', () => { + expect( + run({ + 'core.router': { attachTo: { id: 'other.root', input: 'inputs' } }, + }), + ).toEqual({ id: 'core.router', - at: 'other.root/inputs', + attachTo: { id: 'other.root', input: 'inputs' }, }); expect(() => run({ @@ -331,7 +349,7 @@ describe('expandShorthandExtensionParameters', () => { }, }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'at', 'disabled', 'config'"`, + `"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, ); }); @@ -369,7 +387,7 @@ describe('expandShorthandExtensionParameters', () => { expect(() => run({ 'core.router': { foo: { settings: true } } }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].foo, unknown parameter; expected one of 'at', 'disabled', 'config'"`, + `"Invalid extension configuration at app.extensions[1][core.router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, ); }); }); diff --git a/packages/frontend-app-api/src/wiring/parameters.ts b/packages/frontend-app-api/src/wiring/parameters.ts index efe0fbf678..9474a01d79 100644 --- a/packages/frontend-app-api/src/wiring/parameters.ts +++ b/packages/frontend-app-api/src/wiring/parameters.ts @@ -20,12 +20,12 @@ import { JsonValue } from '@backstage/types'; export interface ExtensionParameters { id: string; - at?: string; + attachTo?: { id: string; input: string }; disabled?: boolean; config?: unknown; } -const knownExtensionParameters = ['at', 'disabled', 'config']; +const knownExtensionParameters = ['attachTo', 'disabled', 'config']; // Since we'll never merge arrays in config the config reader context // isn't too much of a help. Fall back to manual config reading logic @@ -143,15 +143,33 @@ export function expandShorthandExtensionParameters( throw new Error(errorMsg('value must be a boolean or object', id)); } - const at = value.at; + const attachTo = value.attachTo as { id: string; input: string } | undefined; const disabled = value.disabled; const config = value.config; - if (at !== undefined && typeof at !== 'string') { - throw new Error(errorMsg('must be a string', id, 'at')); - } else if (disabled !== undefined && typeof disabled !== 'boolean') { + if (attachTo !== undefined) { + if ( + attachTo === null || + typeof attachTo !== 'object' || + Array.isArray(attachTo) + ) { + throw new Error(errorMsg('must be an object', id, 'attachTo')); + } + if (typeof attachTo.id !== 'string' || attachTo.id === '') { + throw new Error( + errorMsg('must be a non-empty string', id, 'attachTo.id'), + ); + } + if (typeof attachTo.input !== 'string' || attachTo.input === '') { + throw new Error( + errorMsg('must be a non-empty string', id, 'attachTo.input'), + ); + } + } + if (disabled !== undefined && typeof disabled !== 'boolean') { throw new Error(errorMsg('must be a boolean', id, 'disabled')); - } else if ( + } + if ( config !== undefined && (typeof config !== 'object' || config === null || Array.isArray(config)) ) { @@ -175,7 +193,7 @@ export function expandShorthandExtensionParameters( return { id, - at, + attachTo, disabled, config, }; @@ -184,7 +202,7 @@ export function expandShorthandExtensionParameters( export interface ExtensionInstanceParameters { extension: Extension; source?: BackstagePlugin; - at: string; + attachTo: { id: string; input: string }; config?: unknown; } @@ -217,7 +235,7 @@ export function mergeExtensionParameters(options: { extension, params: { source, - at: extension.at, + attachTo: extension.attachTo, disabled: extension.disabled, config: undefined as unknown, }, @@ -226,7 +244,7 @@ export function mergeExtensionParameters(options: { extension, params: { source: undefined, - at: extension.at, + attachTo: extension.attachTo, disabled: extension.disabled, config: undefined as unknown, }, @@ -283,8 +301,8 @@ export function mergeExtensionParameters(options: { ); if (existingIndex !== -1) { const existing = overrides[existingIndex]; - if (overrideParam.at) { - existing.params.at = overrideParam.at; + if (overrideParam.attachTo) { + existing.params.attachTo = overrideParam.attachTo; } if (overrideParam.config) { // TODO: merge config? @@ -309,7 +327,7 @@ export function mergeExtensionParameters(options: { .filter(override => !override.params.disabled) .map(param => ({ extension: param.extension, - at: param.params.at, + attachTo: param.params.attachTo, source: param.params.source, config: param.params.config, })); diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index e28d7d5993..f6781be290 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -136,7 +136,10 @@ export interface CreateExtensionOptions< TConfig, > { // (undocumented) - at: string; + attachTo: { + id: string; + input: string; + }; // (undocumented) configSchema?: PortableSchema; // (undocumented) @@ -182,7 +185,10 @@ export function createPageExtension< } ) & { id: string; - at?: string; + attachTo?: { + id: string; + input: string; + }; disabled?: boolean; inputs?: TInputs; routeRef?: RouteRef; @@ -209,7 +215,10 @@ export interface Extension { // (undocumented) $$type: '@backstage/Extension'; // (undocumented) - at: string; + attachTo: { + id: string; + input: string; + }; // (undocumented) configSchema?: PortableSchema; // (undocumented) diff --git a/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts b/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts index 7426edb766..e90c0b6b85 100644 --- a/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts +++ b/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts @@ -33,7 +33,7 @@ describe('createApiExtension', () => { expect(extension).toEqual({ $$type: '@backstage/Extension', id: 'apis.test', - at: 'core/apis', + attachTo: { id: 'core', input: 'apis' }, disabled: false, configSchema: undefined, inputs: {}, @@ -67,7 +67,7 @@ describe('createApiExtension', () => { expect(extension).toEqual({ $$type: '@backstage/Extension', id: 'apis.test', - at: 'core/apis', + attachTo: { id: 'core', input: 'apis' }, disabled: false, configSchema: undefined, inputs: {}, diff --git a/packages/frontend-plugin-api/src/extensions/createApiExtension.ts b/packages/frontend-plugin-api/src/extensions/createApiExtension.ts index a142a5af61..8e5a9e9f33 100644 --- a/packages/frontend-plugin-api/src/extensions/createApiExtension.ts +++ b/packages/frontend-plugin-api/src/extensions/createApiExtension.ts @@ -51,7 +51,7 @@ export function createApiExtension< return createExtension({ id: `apis.${apiRef.id}`, - at: 'core/apis', + attachTo: { id: 'core', input: 'apis' }, inputs: extensionInputs, configSchema, output: { diff --git a/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx b/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx index 8c55531866..f208c975b7 100644 --- a/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx @@ -31,7 +31,7 @@ export function createNavItemExtension(options: { const { id, routeRef, title, icon } = options; return createExtension({ id, - at: 'core.nav/items', + attachTo: { id: 'core.nav', input: 'items' }, configSchema: createSchemaFromZod(z => z.object({ title: z.string().default(title), diff --git a/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx b/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx index 7de6ee3253..125bf3495d 100644 --- a/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx +++ b/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx @@ -35,7 +35,7 @@ describe('createPageExtension', () => { ).toEqual({ $$type: '@backstage/Extension', id: 'test', - at: 'core.routes/routes', + attachTo: { id: 'core.routes', input: 'routes' }, configSchema: expect.anything(), disabled: false, inputs: {}, @@ -50,7 +50,7 @@ describe('createPageExtension', () => { expect( createPageExtension({ id: 'test', - at: 'other/place', + attachTo: { id: 'other', input: 'place' }, disabled: true, configSchema, inputs: { @@ -63,7 +63,7 @@ describe('createPageExtension', () => { ).toEqual({ $$type: '@backstage/Extension', id: 'test', - at: 'other/place', + attachTo: { id: 'other', input: 'place' }, configSchema: expect.anything(), disabled: true, inputs: { @@ -88,7 +88,7 @@ describe('createPageExtension', () => { ).toEqual({ $$type: '@backstage/Extension', id: 'test', - at: 'core.routes/routes', + attachTo: { id: 'core.routes', input: 'routes' }, configSchema: expect.anything(), disabled: false, inputs: {}, diff --git a/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx b/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx index 5460fb518d..712c55f896 100644 --- a/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx @@ -44,7 +44,7 @@ export function createPageExtension< } ) & { id: string; - at?: string; + attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; routeRef?: RouteRef; @@ -63,7 +63,7 @@ export function createPageExtension< return createExtension({ id: options.id, - at: options.at ?? 'core.routes/routes', + attachTo: options.attachTo ?? { id: 'core.routes', input: 'routes' }, disabled: options.disabled, output: { element: coreExtensionData.reactElement, diff --git a/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts b/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts index 5c13dd199e..38763688a4 100644 --- a/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts +++ b/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts @@ -21,7 +21,7 @@ import { AppTheme } from '@backstage/core-plugin-api'; export function createThemeExtension(theme: AppTheme) { return createExtension({ id: `themes.${theme.id}`, - at: 'core/themes', + attachTo: { id: 'core', input: 'themes' }, output: { theme: coreExtensionData.theme, }, diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 6f4a21399d..757bcd7e3d 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -26,7 +26,7 @@ describe('createExtension', () => { it('should create an extension with a simple output', () => { const extension = createExtension({ id: 'test', - at: 'root', + attachTo: { id: 'root', input: 'default' }, output: { foo: stringData, }, @@ -56,7 +56,7 @@ describe('createExtension', () => { it('should create an extension with a some optional output', () => { const extension = createExtension({ id: 'test', - at: 'root', + attachTo: { id: 'root', input: 'default' }, output: { foo: stringData, bar: stringData.optional(), @@ -94,7 +94,7 @@ describe('createExtension', () => { it('should create an extension with input', () => { const extension = createExtension({ id: 'test', - at: 'root', + attachTo: { id: 'root', input: 'default' }, inputs: { mixed: createExtensionInput({ required: stringData, diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index dd365a87d8..01d4962965 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -80,7 +80,7 @@ export interface CreateExtensionOptions< TConfig, > { id: string; - at: string; + attachTo: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; output: TOutput; @@ -97,7 +97,7 @@ export interface CreateExtensionOptions< export interface Extension { $$type: '@backstage/Extension'; id: string; - at: string; + attachTo: { id: string; input: string }; disabled: boolean; inputs: AnyExtensionInputMap; output: AnyExtensionDataMap; diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts index b14838c17a..589ac59d75 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts @@ -30,7 +30,7 @@ const nameExtensionDataRef = createExtensionDataRef('name'); const TechRadarPage = createExtension({ id: 'plugin.techradar.page', - at: 'test.output/names', + attachTo: { id: 'test.output', input: 'names' }, output: { name: nameExtensionDataRef, }, @@ -41,7 +41,7 @@ const TechRadarPage = createExtension({ const CatalogPage = createExtension({ id: 'plugin.catalog.page', - at: 'test.output/names', + attachTo: { id: 'test.output', input: 'names' }, output: { name: nameExtensionDataRef, }, @@ -55,7 +55,7 @@ const CatalogPage = createExtension({ const TechDocsAddon = createExtension({ id: 'plugin.techdocs.addon.example', - at: 'plugin.techdocs.page/addons', + attachTo: { id: 'plugin.techdocs.page', input: 'addons' }, output: { name: nameExtensionDataRef, }, @@ -69,7 +69,7 @@ const TechDocsAddon = createExtension({ const TechDocsPage = createExtension({ id: 'plugin.techdocs.page', - at: 'test.output/names', + attachTo: { id: 'test.output', input: 'names' }, inputs: { addons: createExtensionInput({ name: nameExtensionDataRef, @@ -85,7 +85,7 @@ const TechDocsPage = createExtension({ const outputExtension = createExtension({ id: 'test.output', - at: 'root', + attachTo: { id: 'root', input: 'default' }, inputs: { names: createExtensionInput({ name: nameExtensionDataRef, diff --git a/plugins/graphiql/src/alpha.tsx b/plugins/graphiql/src/alpha.tsx index 9cd956fc1a..08950b9072 100644 --- a/plugins/graphiql/src/alpha.tsx +++ b/plugins/graphiql/src/alpha.tsx @@ -86,7 +86,7 @@ export function createEndpointExtension(options: { }) { return createExtension({ id: `apis.plugin.graphiql.browse.${options.id}`, - at: 'apis.plugin.graphiql.browse/endpoints', + attachTo: { id: 'apis.plugin.graphiql.browse', input: 'endpoints' }, configSchema: options.configSchema, disabled: options.disabled ?? false, output: { diff --git a/plugins/search-react/alpha-api-report.md b/plugins/search-react/alpha-api-report.md index d62a0f1f0e..9c1248b536 100644 --- a/plugins/search-react/alpha-api-report.md +++ b/plugins/search-react/alpha-api-report.md @@ -48,7 +48,10 @@ export type SearchResultItemExtensionOptions< }, > = { id: string; - at?: string; + attachTo?: { + id: string; + input: string; + }; configSchema?: PortableSchema; component: (options: { config: TConfig; diff --git a/plugins/search-react/src/alpha.test.tsx b/plugins/search-react/src/alpha.test.tsx index b61a400832..8a88c9cc5d 100644 --- a/plugins/search-react/src/alpha.test.tsx +++ b/plugins/search-react/src/alpha.test.tsx @@ -58,7 +58,7 @@ describe('createSearchResultListItemExtension', () => { const TechDocsSearchResultItemExtension = createSearchResultListItemExtension({ id: 'techdocs', - at: 'plugin.search.page/items', + attachTo: { id: 'plugin.search.page', input: 'items' }, configSchema: createSchemaFromZod(z => z.object({ noTrack: z.boolean().default(true), @@ -79,7 +79,7 @@ describe('createSearchResultListItemExtension', () => { const ExploreSearchResultItemExtension = createSearchResultListItemExtension({ id: 'explore', - at: 'plugin.search.page/items', + attachTo: { id: 'plugin.search.page', input: 'items' }, predicate: result => result.type === 'explore', component: async () => ExploreSearchResultItemComponent, }); diff --git a/plugins/search-react/src/alpha.tsx b/plugins/search-react/src/alpha.tsx index 5b44a7bbb5..366112c95e 100644 --- a/plugins/search-react/src/alpha.tsx +++ b/plugins/search-react/src/alpha.tsx @@ -65,7 +65,7 @@ export type SearchResultItemExtensionOptions< /** * The extension attachment point (e.g., search modal or page). */ - at?: string; + attachTo?: { id: string; input: string }; /** * Optional extension config schema. */ @@ -97,7 +97,7 @@ export function createSearchResultListItemExtension< ) as PortableSchema); return createExtension({ id: `plugin.search.result.item.${options.id}`, - at: options.at ?? 'plugin.search.page/items', + attachTo: options.attachTo ?? { id: 'plugin.search.page', input: 'items' }, configSchema, output: { item: searchResultItemExtensionData,