diff --git a/.changeset/slow-bottles-cross.md b/.changeset/slow-bottles-cross.md new file mode 100644 index 0000000000..32446f1554 --- /dev/null +++ b/.changeset/slow-bottles-cross.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-app-api': patch +--- + +Forward ` node`` instead of `extensionId` to resolved extension inputs. diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 0be4f0a0c4..49f6203ce4 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -26,7 +26,7 @@ import { createAppNodeInstance, instantiateAppNodeTree, } from './instantiateAppNodeTree'; -import { AppNodeInstance, AppNodeSpec } from '@backstage/frontend-plugin-api'; +import { AppNodeSpec } from '@backstage/frontend-plugin-api'; import { resolveAppTree } from './resolveAppTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; @@ -85,11 +85,12 @@ function makeNode( function makeInstanceWithId( extension: Extension, config?: TConfig, -): { id: string; instance: AppNodeInstance } { +): AppNode { + const node = makeNode(extension, { config }); return { - id: extension.id, + ...node, instance: createAppNodeInstance({ - node: makeNode(extension, { config }), + node, attachments: new Map(), }), }; @@ -152,8 +153,10 @@ describe('instantiateAppNodeTree', () => { instantiateAppNodeTree(tree.root); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).toBeDefined(); - expect(tree.root.instance?.getData(inputMirrorDataRef)).toEqual({ - test: [{ extensionId: 'child-node', output: { test: 'test' } }], + expect(tree.root.instance?.getData(inputMirrorDataRef)).toMatchObject({ + test: [ + { node: { spec: { id: 'child-node' } }, output: { test: 'test' } }, + ], }); // Multiple calls should have no effect @@ -295,18 +298,21 @@ describe('createAppNodeInstance', () => { }); expect(Array.from(instance.getDataRefs())).toEqual([inputMirrorDataRef]); - expect(instance.getData(inputMirrorDataRef)).toEqual({ + expect(instance.getData(inputMirrorDataRef)).toMatchObject({ optionalSingletonPresent: { - extensionId: 'core/test', + node: { spec: { id: 'core/test' } }, output: { test: 'optionalSingletonPresent' }, }, singleton: { - extensionId: 'core/test', + node: { spec: { id: 'core/test' } }, output: { test: 'singleton', other: 2 }, }, many: [ - { extensionId: 'core/test', output: { test: 'many1' } }, - { extensionId: 'core/test', output: { test: 'many2', other: 3 } }, + { node: { spec: { id: 'core/test' } }, output: { test: 'many1' } }, + { + node: { spec: { id: 'core/test' } }, + output: { test: 'many2', other: 3 }, + }, ], }); }); diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index ca51f10921..dee1c83e3f 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -29,14 +29,14 @@ type Mutable = { function resolveInputData( dataMap: AnyExtensionDataMap, - attachment: { id: string; instance: AppNodeInstance }, + attachment: AppNode, inputName: string, ) { return mapValues(dataMap, ref => { - const value = attachment.instance.getData(ref); + const value = attachment.instance?.getData(ref); if (value === undefined && !ref.config.optional) { throw new Error( - `input '${inputName}' did not receive required extension data '${ref.id}' from extension '${attachment.id}'`, + `input '${inputName}' did not receive required extension data '${ref.id}' from extension '${attachment.spec.id}'`, ); } return value; @@ -45,7 +45,7 @@ function resolveInputData( function resolveInputs( inputMap: AnyExtensionInputMap, - attachments: ReadonlyMap, + attachments: ReadonlyMap, ): ResolvedExtensionInputs { const undeclaredAttachments = Array.from(attachments.entries()).filter( ([inputName]) => inputMap[inputName] === undefined, @@ -59,7 +59,7 @@ function resolveInputs( .map( ([k, exts]) => `'${k}' from extension${exts.length > 1 ? 's' : ''} '${exts - .map(e => e.id) + .map(e => e.spec.id) .join("', '")}'`, ) .join(' and ')}`, @@ -71,7 +71,7 @@ function resolveInputs( if (input.config.singleton) { if (attachedNodes.length > 1) { - const attachedNodeIds = attachedNodes.map(e => e.id); + const attachedNodeIds = attachedNodes.map(e => e.spec.id); throw Error( `expected ${ input.config.optional ? 'at most' : 'exactly' @@ -86,7 +86,7 @@ function resolveInputs( throw Error(`input '${inputName}' is required but was not received`); } return { - extensionId: attachedNodes[0].id, + node: attachedNodes[0], output: resolveInputData( input.extensionData, attachedNodes[0], @@ -96,7 +96,7 @@ function resolveInputs( } return attachedNodes.map(attachment => ({ - extensionId: attachment.id, + node: attachment, output: resolveInputData(input.extensionData, attachment, inputName), })); }) as ResolvedExtensionInputs; @@ -105,7 +105,7 @@ function resolveInputs( /** @internal */ export function createAppNodeInstance(options: { node: AppNode; - attachments: ReadonlyMap; + attachments: ReadonlyMap; }): AppNodeInstance { const { node, attachments } = options; const { id, extension, config } = node.spec; @@ -172,10 +172,7 @@ export function instantiateAppNodeTree(rootNode: AppNode): void { return undefined; } - const instantiatedAttachments = new Map< - string, - { id: string; instance: AppNodeInstance }[] - >(); + const instantiatedAttachments = new Map(); for (const [input, children] of node.edges.attachments) { const instantiatedChildren = children.flatMap(child => { @@ -183,7 +180,7 @@ export function instantiateAppNodeTree(rootNode: AppNode): void { if (!childInstance) { return []; } - return [{ id: child.spec.id, instance: childInstance }]; + return [child]; }); if (instantiatedChildren.length > 0) { instantiatedAttachments.set(input, instantiatedChildren); diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index e8f4f16e97..9435a239cb 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -888,7 +888,7 @@ export { ProfileInfoApi }; // @public export type ResolvedExtensionInput = { - extensionId: string; + node: AppNode; output: ExtensionDataValues; }; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index cac2ac7b49..ccb93f44ea 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -57,7 +57,7 @@ export type ExtensionDataValues = { */ export type ResolvedExtensionInput = { - extensionId: string; + node: AppNode; output: ExtensionDataValues; };