diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 68a95fa1fe..802c71f729 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -15,6 +15,7 @@ */ import { + AppNode, Extension, createExtension, createExtensionDataRef, @@ -52,15 +53,27 @@ const simpleExtension = createExtension({ function makeSpec( extension: Extension, - config?: TConfig, + spec?: Partial, ): AppNodeSpec { return { id: extension.id, attachTo: extension.attachTo, disabled: extension.disabled, extension, - config, source: undefined, + ...spec, + }; +} + +function makeNode( + extension: Extension, + spec?: Partial, +): AppNode { + return { + spec: makeSpec(extension, spec), + edges: { + attachments: new Map(), + }, }; } @@ -71,7 +84,7 @@ function makeInstanceWithId( return { id: extension.id, instance: createAppNodeInstance({ - spec: makeSpec(extension, config), + node: makeNode(extension, { config }), attachments: new Map(), }), }; @@ -80,7 +93,7 @@ function makeInstanceWithId( describe('instantiateAppNodeTree', () => { it('should instantiate a single node', () => { const tree = resolveAppTree('root-node', [ - { ...makeSpec(simpleExtension), id: 'root-node' }, + makeSpec(simpleExtension, { id: 'root-node' }), ]); expect(tree.root.instance).not.toBeDefined(); instantiateAppNodeTree(tree.root); @@ -94,7 +107,7 @@ describe('instantiateAppNodeTree', () => { it('should not instantiate disabled nodes', () => { const tree = resolveAppTree('root-node', [ - { ...makeSpec(simpleExtension), id: 'root-node', disabled: true }, + makeSpec(simpleExtension, { id: 'root-node', disabled: true }), ]); expect(tree.root.instance).not.toBeDefined(); instantiateAppNodeTree(tree.root); @@ -103,28 +116,25 @@ describe('instantiateAppNodeTree', () => { it('should instantiate a node with attachments', () => { const tree = resolveAppTree('root-node', [ - { - ...makeSpec( - createExtension({ - id: 'root-node', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - test: createExtensionInput({ test: testDataRef }), - }, - output: { - inputMirror: inputMirrorDataRef, - }, - factory({ inputs }) { - return { inputMirror: inputs }; - }, - }), - ), - }, - { - ...makeSpec(simpleExtension), + makeSpec( + createExtension({ + id: 'root-node', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput({ test: testDataRef }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), + makeSpec(simpleExtension, { id: 'child-node', attachTo: { id: 'root-node', input: 'test' }, - }, + }), ]); const childNode = tree.nodes.get('child-node'); @@ -191,7 +201,7 @@ describe('createAppNodeInstance', () => { it('should create a simple extension instance', () => { const attachments = new Map(); const instance = createAppNodeInstance({ - spec: makeSpec(simpleExtension), + node: makeNode(simpleExtension), attachments, }); @@ -231,7 +241,7 @@ describe('createAppNodeInstance', () => { ]); const instance = createAppNodeInstance({ attachments, - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -283,10 +293,7 @@ describe('createAppNodeInstance', () => { it('should refuse to create an extension with invalid config', () => { expect(() => createAppNodeInstance({ - spec: { - ...makeSpec(simpleExtension), - config: { other: 'not-a-number' }, - }, + node: makeNode(simpleExtension, { config: { other: 'not-a-number' } }), attachments: new Map(), }), ).toThrow( @@ -297,7 +304,7 @@ describe('createAppNodeInstance', () => { it('should forward extension factory errors', () => { expect(() => createAppNodeInstance({ - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -319,7 +326,7 @@ describe('createAppNodeInstance', () => { it('should refuse to create an instance with duplicate output', () => { expect(() => createAppNodeInstance({ - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -342,7 +349,7 @@ describe('createAppNodeInstance', () => { it('should refuse to create an instance with disconnected output data', () => { expect(() => createAppNodeInstance({ - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -364,7 +371,7 @@ describe('createAppNodeInstance', () => { it('should refuse to create an instance with missing required input', () => { expect(() => createAppNodeInstance({ - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -408,7 +415,7 @@ describe('createAppNodeInstance', () => { ], ], ]), - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -443,7 +450,7 @@ describe('createAppNodeInstance', () => { ], ], ]), - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -469,7 +476,7 @@ describe('createAppNodeInstance', () => { ], ], ]), - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -503,7 +510,7 @@ describe('createAppNodeInstance', () => { ], ], ]), - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, @@ -531,7 +538,7 @@ describe('createAppNodeInstance', () => { attachments: new Map([ ['singleton', [makeInstanceWithId(simpleExtension, undefined)]], ]), - spec: makeSpec( + node: makeNode( createExtension({ id: 'core.test', attachTo: { id: 'ignored', input: 'ignored' }, diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 982681cac6..2b1ab04c25 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -20,11 +20,7 @@ import { ExtensionDataRef, } from '@backstage/frontend-plugin-api'; import mapValues from 'lodash/mapValues'; -import { - AppNode, - AppNodeInstance, - AppNodeSpec, -} from '@backstage/frontend-plugin-api'; +import { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api'; type Mutable = { -readonly [P in keyof T]: T[P]; @@ -99,11 +95,11 @@ function resolveInputs( /** @internal */ export function createAppNodeInstance(options: { - spec: AppNodeSpec; + node: AppNode; attachments: ReadonlyMap; }): AppNodeInstance { - const { spec, attachments } = options; - const { id, extension, config } = spec; + const { node, attachments } = options; + const { id, extension, config } = node.spec; const extensionData = new Map(); const extensionDataRefs = new Set>(); @@ -118,7 +114,7 @@ export function createAppNodeInstance(options: { try { const namedOutputs = extension.factory({ - spec, + node, config: parsedConfig, inputs: resolveInputs(extension.inputs, attachments), }); @@ -186,7 +182,7 @@ export function instantiateAppNodeTree(rootNode: AppNode): void { } (node as Mutable).instance = createAppNodeInstance({ - spec: node.spec, + node, attachments: instantiatedAttachments, }); diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx index 7010edd2f5..8fad8d8336 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx @@ -20,7 +20,7 @@ import { ErrorBoundary } from './ErrorBoundary'; import { ExtensionSuspense } from './ExtensionSuspense'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker'; -import { AppNodeSpec } from '../apis'; +import { AppNode } from '../apis'; type RouteTrackerProps = PropsWithChildren<{ disableTracking?: boolean; @@ -44,24 +44,24 @@ const RouteTracker = (props: RouteTrackerProps) => { /** @public */ export interface ExtensionBoundaryProps { - spec: AppNodeSpec; + node: AppNode; routable?: boolean; children: ReactNode; } /** @public */ export function ExtensionBoundary(props: ExtensionBoundaryProps) { - const { spec, routable, children } = props; + const { node, routable, children } = props; // Skipping "routeRef" attribute in the new system, the extension "id" should provide more insight const attributes = { - extension: spec.id, - pluginId: spec.source?.id, + extension: node.spec.id, + pluginId: node.spec.source?.id, }; return ( - + {children} diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 452669367e..9443caeb66 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { AppNodeSpec } from '../apis'; +import { AppNode } from '../apis'; import { PortableSchema } from '../schema'; import { Expand } from '../types'; import { ExtensionDataRef } from './createExtensionDataRef'; @@ -80,7 +80,7 @@ export interface CreateExtensionOptions< output: TOutput; configSchema?: PortableSchema; factory(options: { - spec: AppNodeSpec; + node: AppNode; config: TConfig; inputs: Expand>; }): Expand>; @@ -96,7 +96,7 @@ export interface Extension { output: AnyExtensionDataMap; configSchema?: PortableSchema; factory(options: { - spec: AppNodeSpec; + node: AppNode; config: TConfig; inputs: Record< string,