From 89f2300d3dd9056d0bc4c619630002bd76381b96 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Sep 2024 12:03:13 +0200 Subject: [PATCH 1/6] frontend-plugin-api: add ability to override params from .make Signed-off-by: Patrik Oldsberg --- .../src/wiring/createExtension.test.ts | 29 ++++ .../src/wiring/createExtension.ts | 134 ++++++++++-------- .../wiring/createExtensionBlueprint.test.tsx | 113 +++++++++++++++ .../src/wiring/createExtensionBlueprint.ts | 12 +- 4 files changed, 227 insertions(+), 61 deletions(-) diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index b5d846871f..e07647b644 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -744,6 +744,35 @@ describe('createExtension', () => { ); }); + it('should not have params unless explicitly defined', () => { + const ext = createExtension({ + attachTo: { id: 'root', input: 'blob' }, + output: [stringDataRef], + factory() { + return [stringDataRef('0')]; + }, + }); + + ext.override({ + // @ts-expect-error - params are not allowed + params: {} as any, + }); + + ext.override({ + // @ts-expect-error - params are not provided + factory(origFactory, { params }) { + return origFactory({ + // @ts-expect-error - params are not allowed + params: { + ...params, + }, + }); + }, + }); + + expect(ext).toBeDefined(); + }); + it('should be able to override input values', () => { const outputRef = createExtensionDataRef().with({ id: 'output', diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index ac7b1de432..97cb47a4fd 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -155,6 +155,7 @@ export type ExtensionDefinitionParameters = { { optional: boolean; singleton: boolean } >; }; + params?: object; }; /** @public */ @@ -177,42 +178,53 @@ export type ExtensionDefinition< >; }, >( - args: { - attachTo?: { id: string; input: string }; - disabled?: boolean; - inputs?: TExtraInputs & { - [KName in keyof T['inputs']]?: `Error: Input '${KName & - string}' is already defined in parent definition`; - }; - output?: Array; - config?: { - schema: TExtensionConfigSchema & { - [KName in keyof T['config']]?: `Error: Config key '${KName & - string}' is already defined in parent schema`; + args: Expand< + { + attachTo?: { id: string; input: string }; + disabled?: boolean; + inputs?: TExtraInputs & { + [KName in keyof T['inputs']]?: `Error: Input '${KName & + string}' is already defined in parent definition`; }; - }; - factory?( - originalFactory: (context?: { - config?: T['config']; - inputs?: ResolveInputValueOverrides>; - }) => ExtensionDataContainer>, - context: { - node: AppNode; - apis: ApiHolder; - config: T['config'] & { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; + output?: Array; + config?: { + schema: TExtensionConfigSchema & { + [KName in keyof T['config']]?: `Error: Config key '${KName & + string}' is already defined in parent schema`; }; - inputs: Expand>; - }, - ): Iterable; - } & VerifyExtensionFactoryOutput< - AnyExtensionDataRef extends UNewOutput - ? NonNullable - : UNewOutput, - UFactoryOutput - >, + }; + factory?( + originalFactory: ( + context?: Expand< + { + config?: T['config']; + inputs?: ResolveInputValueOverrides>; + } & ([T['params']] extends [never] + ? {} + : { params?: Partial }) + >, + ) => ExtensionDataContainer>, + context: { + node: AppNode; + apis: ApiHolder; + config: T['config'] & { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }; + inputs: Expand>; + }, + ): Iterable; + } & ([T['params']] extends [never] + ? {} + : { params?: Partial }) + > & + VerifyExtensionFactoryOutput< + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UFactoryOutput + >, ): ExtensionDefinition<{ kind: T['kind']; namespace: T['namespace']; @@ -274,6 +286,7 @@ export function createExtension< >; output: UOutput; inputs: TInputs; + params: never; kind: string | undefined extends TKind ? undefined : TKind; namespace: string | undefined extends TNamespace ? undefined : TNamespace; name: string | undefined extends TName ? undefined : TName; @@ -320,6 +333,7 @@ export function createExtension< >; output: UOutput; inputs: TInputs; + params: never; kind: string | undefined extends TKind ? undefined : TKind; namespace: string | undefined extends TNamespace ? undefined : TNamespace; name: string | undefined extends TName ? undefined : TName; @@ -362,6 +376,7 @@ export function createExtension< >; output: UOutput; inputs: TInputs; + params: object; kind: string | undefined extends TKind ? undefined : TKind; namespace: string | undefined extends TNamespace ? undefined : TNamespace; name: string | undefined extends TName ? undefined : TName; @@ -428,15 +443,6 @@ export function createExtension< 'Cannot override an extension that is not declared using the new format with outputs as an array', ); } - const newOptions = options as CreateExtensionOptions< - TKind, - TNamespace, - TName, - UOutput, - TInputs, - TConfigSchema, - UFactoryOutput - >; // TODO(Rugvip): Making this a type check would be optimal, but it seems // like it's tricky to add that and still have the type @@ -446,48 +452,60 @@ export function createExtension< 'Refused to override output without also overriding factory', ); } + // TODO(Rugvip): Similar to above, would be nice to error during type checking, but don't want to complicate the types too much + if (overrideOptions.params && overrideOptions.factory) { + throw new Error( + 'Refused to override params and factory at the same time', + ); + } return createExtension({ - kind: newOptions.kind, - namespace: newOptions.namespace, - name: newOptions.name, - attachTo: overrideOptions.attachTo ?? newOptions.attachTo, - disabled: overrideOptions.disabled ?? newOptions.disabled, - inputs: { ...overrideOptions.inputs, ...newOptions.inputs }, + kind: options.kind, + namespace: options.namespace, + name: options.name, + attachTo: overrideOptions.attachTo ?? options.attachTo, + disabled: overrideOptions.disabled ?? options.disabled, + inputs: { ...overrideOptions.inputs, ...options.inputs }, output: (overrideOptions.output ?? - newOptions.output) as AnyExtensionDataRef[], + options.output) as AnyExtensionDataRef[], config: - newOptions.config || overrideOptions.config + options.config || overrideOptions.config ? { schema: { - ...newOptions.config?.schema, + ...options.config?.schema, ...overrideOptions.config?.schema, }, } : undefined, factory: ({ node, apis, config, inputs }) => { if (!overrideOptions.factory) { - return newOptions.factory({ + return options.factory({ node, apis, config: config as any, inputs: inputs as any, - }); + params: overrideOptions.params, + // TODO(Rugvip): This is a bit of a hack to send the params + // through to the blueprint factory, might be that there's a + // better way to do this + } as Parameters[0]); } const parentResult = overrideOptions.factory( (innerContext): ExtensionDataContainer => { return createExtensionDataContainer( - newOptions.factory({ + options.factory({ node, apis, config: (innerContext?.config ?? config) as any, inputs: resolveInputOverrides( - newOptions.inputs, + options.inputs, inputs, innerContext?.inputs, ) as any, - }) as Iterable, - newOptions.output, + params: innerContext?.params, + // TODO(Rugvip): Same as above + } as Parameters[0]) as Iterable, + options.output, ); }, { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index 16c763d725..79ccce31ff 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -836,4 +836,117 @@ describe('createExtensionBlueprint', () => { ), ).toEqual([testDataRef1('foo'), testDataRef2('bar')]); }); + + it('should be possible to override extensions resulting from .make', () => { + const testDataRef1 = createExtensionDataRef().with({ id: 'test1' }); + const testDataRef2 = createExtensionDataRef().with({ id: 'test2' }); + + function getOutputs(ext: ExtensionDefinition) { + const tester = createExtensionTester(ext); + return { + test1: tester.get(testDataRef1), + test2: tester.get(testDataRef2), + }; + } + + const blueprint = createExtensionBlueprint({ + kind: 'test-extension', + attachTo: { id: 'test', input: 'default' }, + output: [testDataRef1, testDataRef2], + *factory(params: { test1: string; test2: string }) { + yield testDataRef1(params.test1); + yield testDataRef2(params.test2); + }, + }); + + const extension = blueprint.make({ + params: { + test1: 'orig-1', + test2: 'orig-2', + }, + }); + + expect(getOutputs(extension)).toEqual({ + test1: 'orig-1', + test2: 'orig-2', + }); + + expect( + getOutputs( + extension.override({ + params: { + test1: 'override-1', + test2: 'override-2', + }, + }), + ), + ).toEqual({ + test1: 'override-1', + test2: 'override-2', + }); + + // Partial override + expect( + getOutputs( + extension.override({ + params: { + test2: 'override-2', + }, + }), + ), + ).toEqual({ + test1: 'orig-1', + test2: 'override-2', + }); + + expect( + getOutputs( + extension.override({ + factory(origFactory) { + return origFactory({ + params: { + test1: 'override-1', + test2: 'override-2', + }, + }); + }, + }), + ), + ).toEqual({ + test1: 'override-1', + test2: 'override-2', + }); + + // Partial override via factory + expect( + getOutputs( + extension.override({ + factory(origFactory) { + return origFactory({ + params: { + test2: 'override-2', + }, + }); + }, + }), + ), + ).toEqual({ + test1: 'orig-1', + test2: 'override-2', + }); + + expect(() => + getOutputs( + extension.override({ + params: { + test1: 'override-1', + test2: 'override-2', + }, + factory(origFactory) { + return origFactory(); + }, + }), + ), + ).toThrow('Refused to override params and factory at the same time'); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 40d1284b15..fb9a4de830 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -124,6 +124,7 @@ export interface ExtensionBlueprint< configInput: T['configInput']; output: T['output']; inputs: T['inputs']; + params: T['params']; }>; /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ make< @@ -232,6 +233,7 @@ export interface ExtensionBlueprint< kind: T['kind']; namespace: undefined; name: string | undefined extends TNewName ? T['name'] : TNewName; + params: T['params']; }>; /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ makeWithOverrides< @@ -479,9 +481,13 @@ export function createExtensionBlueprint< output: options.output as AnyExtensionDataRef[], config: options.config, factory: ctx => - options.factory(args.params, ctx) as Iterable< - ExtensionDataValue - >, + options.factory( + // TODO(Rugvip): The `ctx` here might actually have a `params` key + // when the extension has been overridden. It's currently hidden in + // the types and there might be a better way to do this. + { ...args.params, ...(ctx as any).params }, + ctx, + ) as Iterable>, }) as ExtensionDefinition; }, makeWithOverrides(args) { From 7ac5d97295802f6e937a0d647b727ef094b3272d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Sep 2024 12:07:30 +0200 Subject: [PATCH 2/6] frontend-plugin-api: add ability to override params from .makeWithOverrides Signed-off-by: Patrik Oldsberg --- .../wiring/createExtensionBlueprint.test.tsx | 115 ++++++++++++++++++ .../src/wiring/createExtensionBlueprint.ts | 27 ++-- 2 files changed, 131 insertions(+), 11 deletions(-) diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index 79ccce31ff..16786f9757 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -949,4 +949,119 @@ describe('createExtensionBlueprint', () => { ), ).toThrow('Refused to override params and factory at the same time'); }); + + it('should be possible to override extensions resulting from .makeWithOverrides', () => { + const testDataRef1 = createExtensionDataRef().with({ id: 'test1' }); + const testDataRef2 = createExtensionDataRef().with({ id: 'test2' }); + + function getOutputs(ext: ExtensionDefinition) { + const tester = createExtensionTester(ext); + return { + test1: tester.get(testDataRef1), + test2: tester.get(testDataRef2), + }; + } + + const blueprint = createExtensionBlueprint({ + kind: 'test-extension', + attachTo: { id: 'test', input: 'default' }, + output: [testDataRef1, testDataRef2], + *factory(params: { test1: string; test2: string }) { + yield testDataRef1(params.test1); + yield testDataRef2(params.test2); + }, + }); + + const extension = blueprint.makeWithOverrides({ + factory(origFactory) { + return origFactory({ + test1: 'orig-1', + test2: 'orig-2', + }); + }, + }); + + expect(getOutputs(extension)).toEqual({ + test1: 'orig-1', + test2: 'orig-2', + }); + + expect( + getOutputs( + extension.override({ + params: { + test1: 'override-1', + test2: 'override-2', + }, + }), + ), + ).toEqual({ + test1: 'override-1', + test2: 'override-2', + }); + + // Partial override + expect( + getOutputs( + extension.override({ + params: { + test2: 'override-2', + }, + }), + ), + ).toEqual({ + test1: 'orig-1', + test2: 'override-2', + }); + + expect( + getOutputs( + extension.override({ + factory(origFactory) { + return origFactory({ + params: { + test1: 'override-1', + test2: 'override-2', + }, + }); + }, + }), + ), + ).toEqual({ + test1: 'override-1', + test2: 'override-2', + }); + + // Partial override via factory + expect( + getOutputs( + extension.override({ + factory(origFactory) { + return origFactory({ + params: { + test2: 'override-2', + }, + }); + }, + }), + ), + ).toEqual({ + test1: 'orig-1', + test2: 'override-2', + }); + + expect(() => + getOutputs( + extension.override({ + params: { + test1: 'override-1', + test2: 'override-2', + }, + factory(origFactory) { + return origFactory(); + }, + }), + ), + ).toThrow('Refused to override params and factory at the same time'); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index fb9a4de830..3436464711 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -508,20 +508,25 @@ export function createExtensionBlueprint< }, } : undefined, - factory: ({ node, config, inputs, apis }) => { + factory: ctx => { + const { node, config, inputs, apis } = ctx; return args.factory( (innerParams, innerContext) => { return createExtensionDataContainer( - options.factory(innerParams, { - apis, - node, - config: (innerContext?.config ?? config) as any, - inputs: resolveInputOverrides( - options.inputs, - inputs, - innerContext?.inputs, - ) as any, - }) as Iterable, + options.factory( + // TODO(Rugvip): Same as ctx.params from .make + { ...innerParams, ...(ctx as any).params }, + { + apis, + node, + config: (innerContext?.config ?? config) as any, + inputs: resolveInputOverrides( + options.inputs, + inputs, + innerContext?.inputs, + ) as any, + }, + ) as Iterable, options.output, ); }, From ac30b57ae64c8400ff7a93f9f53ae5559dd54bad Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Sep 2024 12:15:12 +0200 Subject: [PATCH 3/6] frontend-plugin-api: refactor to use symbol to pass param overrides Signed-off-by: Patrik Oldsberg --- .../src/wiring/createExtension.ts | 18 ++++++++++-------- .../src/wiring/createExtensionBlueprint.ts | 9 +++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 97cb47a4fd..e35540e310 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -33,6 +33,12 @@ import { z } from 'zod'; import { createSchemaFromZod } from '../schema/createSchemaFromZod'; import { OpaqueExtensionDefinition } from '@internal/frontend'; +/** + * This symbol is used to pass parameter overrides from the extension override to the blueprint factory + * @internal + */ +export const ctxParamsSymbol = Symbol('params'); + /** * Convert a single extension input into a matching resolved input. * @public @@ -484,11 +490,8 @@ export function createExtension< apis, config: config as any, inputs: inputs as any, - params: overrideOptions.params, - // TODO(Rugvip): This is a bit of a hack to send the params - // through to the blueprint factory, might be that there's a - // better way to do this - } as Parameters[0]); + [ctxParamsSymbol as any]: overrideOptions.params, + }); } const parentResult = overrideOptions.factory( (innerContext): ExtensionDataContainer => { @@ -502,9 +505,8 @@ export function createExtension< inputs, innerContext?.inputs, ) as any, - params: innerContext?.params, - // TODO(Rugvip): Same as above - } as Parameters[0]) as Iterable, + [ctxParamsSymbol as any]: innerContext?.params, + }) as Iterable, options.output, ); }, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 3436464711..03e78ceb58 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -21,6 +21,7 @@ import { ResolvedExtensionInputs, VerifyExtensionFactoryOutput, createExtension, + ctxParamsSymbol, } from './createExtension'; import { z } from 'zod'; import { ExtensionInput } from './createExtensionInput'; @@ -482,10 +483,7 @@ export function createExtensionBlueprint< config: options.config, factory: ctx => options.factory( - // TODO(Rugvip): The `ctx` here might actually have a `params` key - // when the extension has been overridden. It's currently hidden in - // the types and there might be a better way to do this. - { ...args.params, ...(ctx as any).params }, + { ...args.params, ...(ctx as any)[ctxParamsSymbol] }, ctx, ) as Iterable>, }) as ExtensionDefinition; @@ -514,8 +512,7 @@ export function createExtensionBlueprint< (innerParams, innerContext) => { return createExtensionDataContainer( options.factory( - // TODO(Rugvip): Same as ctx.params from .make - { ...innerParams, ...(ctx as any).params }, + { ...innerParams, ...(ctx as any)[ctxParamsSymbol] }, { apis, node, From 323aae85e70fa216dd5907cc390e0a386573a9ad Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Sep 2024 12:19:17 +0200 Subject: [PATCH 4/6] changesets: changeset for blueprint param overrides Signed-off-by: Patrik Oldsberg --- .changeset/large-plants-rhyme.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .changeset/large-plants-rhyme.md diff --git a/.changeset/large-plants-rhyme.md b/.changeset/large-plants-rhyme.md new file mode 100644 index 0000000000..3566826730 --- /dev/null +++ b/.changeset/large-plants-rhyme.md @@ -0,0 +1,30 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +It is now possible to override the blueprint parameters when overriding an extension created from a blueprint: + +```ts +const myExtension = MyBlueprint.make({ + params: { + myParam: 'myDefault', + }, +}); + +const myOverride = myExtension.override({ + params: { + myParam: 'myOverride', + }, +}); +const myFactoryOverride = myExtension.override({ + factory(origFactory) { + return origFactory({ + params: { + myParam: 'myOverride', + }, + }); + }, +}); +``` + +The provided parameters will be merged with the original parameters of the extension. From 0a5ec3466859240a38ff08a75768fa2114de1d15 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Sep 2024 12:39:47 +0200 Subject: [PATCH 5/6] update API reports for extension param overrides Signed-off-by: Patrik Oldsberg --- .../app-next-example-plugin/api-report.md | 5 + packages/frontend-plugin-api/api-report.md | 95 +++++++++++------- plugins/api-docs/api-report-alpha.md | 51 ++++++++++ plugins/app-visualizer/api-report.md | 10 ++ plugins/app/api-report.md | 89 +++++++++++++++++ plugins/catalog-graph/api-report-alpha.md | 9 ++ plugins/catalog-import/api-report-alpha.md | 8 ++ plugins/catalog/api-report-alpha.md | 97 +++++++++++++++++++ plugins/devtools/api-report-alpha.md | 13 +++ plugins/home/api-report-alpha.md | 5 + plugins/kubernetes/api-report-alpha.md | 24 +++++ plugins/org/api-report-alpha.md | 16 +++ plugins/scaffolder/api-report-alpha.md | 13 +++ plugins/search/api-report-alpha.md | 26 +++++ plugins/techdocs/api-report-alpha.md | 32 ++++++ plugins/user-settings/api-report-alpha.md | 15 +++ 16 files changed, 471 insertions(+), 37 deletions(-) diff --git a/packages/app-next-example-plugin/api-report.md b/packages/app-next-example-plugin/api-report.md index 06d325b844..3f8b821559 100644 --- a/packages/app-next-example-plugin/api-report.md +++ b/packages/app-next-example-plugin/api-report.md @@ -40,6 +40,11 @@ const examplePlugin: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; } >; diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index fc62582af0..1b202098cb 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -453,6 +453,7 @@ export function createComponentExtension(options: { } >; }; + params: never; kind: 'component'; namespace: undefined; name: string; @@ -520,6 +521,7 @@ export function createExtension< >; output: UOutput; inputs: TInputs; + params: never; kind: string | undefined extends TKind ? undefined : TKind; namespace: string | undefined extends TNamespace ? undefined : TNamespace; name: string | undefined extends TName ? undefined : TName; @@ -569,6 +571,7 @@ export function createExtension< >; output: UOutput; inputs: TInputs; + params: never; kind: string | undefined extends TKind ? undefined : TKind; namespace: string | undefined extends TNamespace ? undefined : TNamespace; name: string | undefined extends TName ? undefined : TName; @@ -983,6 +986,7 @@ export interface ExtensionBlueprint< configInput: T['configInput']; output: T['output']; inputs: T['inputs']; + params: T['params']; }>; // @deprecated (undocumented) make< @@ -1093,6 +1097,7 @@ export interface ExtensionBlueprint< kind: T['kind']; namespace: undefined; name: string | undefined extends TNewName ? T['name'] : TNewName; + params: T['params']; }>; // @deprecated (undocumented) makeWithOverrides< @@ -1306,45 +1311,60 @@ export type ExtensionDefinition< >; }, >( - args: { - attachTo?: { - id: string; - input: string; - }; - disabled?: boolean; - inputs?: TExtraInputs & { - [KName in keyof T['inputs']]?: `Error: Input '${KName & - string}' is already defined in parent definition`; - }; - output?: Array; - config?: { - schema: TExtensionConfigSchema & { - [KName in keyof T['config']]?: `Error: Config key '${KName & - string}' is already defined in parent schema`; + args: Expand< + { + attachTo?: { + id: string; + input: string; }; - }; - factory?( - originalFactory: (context?: { - config?: T['config']; - inputs?: ResolveInputValueOverrides>; - }) => ExtensionDataContainer>, - context: { - node: AppNode; - apis: ApiHolder; - config: T['config'] & { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; + disabled?: boolean; + inputs?: TExtraInputs & { + [KName in keyof T['inputs']]?: `Error: Input '${KName & + string}' is already defined in parent definition`; + }; + output?: Array; + config?: { + schema: TExtensionConfigSchema & { + [KName in keyof T['config']]?: `Error: Config key '${KName & + string}' is already defined in parent schema`; }; - inputs: Expand>; - }, - ): Iterable; - } & VerifyExtensionFactoryOutput< - AnyExtensionDataRef extends UNewOutput - ? NonNullable - : UNewOutput, - UFactoryOutput - >, + }; + factory?( + originalFactory: ( + context?: Expand< + { + config?: T['config']; + inputs?: ResolveInputValueOverrides>; + } & ([T['params']] extends [never] + ? {} + : { + params?: Partial; + }) + >, + ) => ExtensionDataContainer>, + context: { + node: AppNode; + apis: ApiHolder; + config: T['config'] & { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }; + inputs: Expand>; + }, + ): Iterable; + } & ([T['params']] extends [never] + ? {} + : { + params?: Partial; + }) + > & + VerifyExtensionFactoryOutput< + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UFactoryOutput + >, ): ExtensionDefinition<{ kind: T['kind']; namespace: T['namespace']; @@ -1388,6 +1408,7 @@ export type ExtensionDefinitionParameters = { } >; }; + params?: object; }; // @public (undocumented) diff --git a/plugins/api-docs/api-report-alpha.md b/plugins/api-docs/api-report-alpha.md index bb2da7bc0b..9800406be3 100644 --- a/plugins/api-docs/api-report-alpha.md +++ b/plugins/api-docs/api-report-alpha.md @@ -41,6 +41,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; 'api:api-docs/config': ExtensionDefinition<{ kind: 'api'; @@ -54,6 +59,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'page:api-docs': ExtensionDefinition<{ config: { @@ -92,6 +100,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'entity-card:api-docs/has-apis': ExtensionDefinition<{ kind: 'entity-card'; @@ -124,6 +137,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:api-docs/definition': ExtensionDefinition<{ kind: 'entity-card'; @@ -156,6 +173,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:api-docs/consumed-apis': ExtensionDefinition<{ kind: 'entity-card'; @@ -188,6 +209,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:api-docs/provided-apis': ExtensionDefinition<{ kind: 'entity-card'; @@ -220,6 +245,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:api-docs/consuming-components': ExtensionDefinition<{ kind: 'entity-card'; @@ -252,6 +281,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:api-docs/providing-components': ExtensionDefinition<{ kind: 'entity-card'; @@ -284,6 +317,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-content:api-docs/definition': ExtensionDefinition<{ kind: 'entity-content'; @@ -333,6 +370,13 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + defaultPath: string; + defaultTitle: string; + routeRef?: RouteRef | undefined; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-content:api-docs/apis': ExtensionDefinition<{ kind: 'entity-content'; @@ -382,6 +426,13 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + defaultPath: string; + defaultTitle: string; + routeRef?: RouteRef | undefined; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; } >; diff --git a/plugins/app-visualizer/api-report.md b/plugins/app-visualizer/api-report.md index e542674845..3f3813f5ee 100644 --- a/plugins/app-visualizer/api-report.md +++ b/plugins/app-visualizer/api-report.md @@ -41,6 +41,11 @@ const visualizerPlugin: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'nav-item:app-visualizer': ExtensionDefinition<{ kind: 'nav-item'; @@ -58,6 +63,11 @@ const visualizerPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; } >; diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md index 6f2e6e0463..e6ef06eebf 100644 --- a/plugins/app/api-report.md +++ b/plugins/app/api-report.md @@ -46,6 +46,7 @@ const appPlugin: FrontendPlugin< } >; }; + params: never; kind: undefined; namespace: undefined; name: undefined; @@ -62,6 +63,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'app/layout': ExtensionDefinition<{ config: {}; @@ -87,6 +91,7 @@ const appPlugin: FrontendPlugin< } >; }; + params: never; kind: undefined; namespace: undefined; name: 'layout'; @@ -130,6 +135,7 @@ const appPlugin: FrontendPlugin< } >; }; + params: never; kind: undefined; namespace: undefined; name: 'nav'; @@ -195,6 +201,7 @@ const appPlugin: FrontendPlugin< } >; }; + params: never; kind: undefined; namespace: undefined; name: 'root'; @@ -224,6 +231,7 @@ const appPlugin: FrontendPlugin< } >; }; + params: never; kind: undefined; namespace: undefined; name: 'routes'; @@ -248,6 +256,9 @@ const appPlugin: FrontendPlugin< kind: 'api'; namespace: undefined; name: 'app-theme'; + params: { + factory: AnyApiFactory; + }; }>; 'theme:app/light': ExtensionDefinition<{ kind: 'theme'; @@ -257,6 +268,9 @@ const appPlugin: FrontendPlugin< configInput: {}; output: ConfigurableExtensionDataRef; inputs: {}; + params: { + theme: AppTheme; + }; }>; 'theme:app/dark': ExtensionDefinition<{ kind: 'theme'; @@ -266,6 +280,9 @@ const appPlugin: FrontendPlugin< configInput: {}; output: ConfigurableExtensionDataRef; inputs: {}; + params: { + theme: AppTheme; + }; }>; 'api:app/components': ExtensionDefinition<{ config: {}; @@ -294,6 +311,9 @@ const appPlugin: FrontendPlugin< kind: 'api'; namespace: undefined; name: 'components'; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/icons': ExtensionDefinition<{ config: {}; @@ -321,6 +341,9 @@ const appPlugin: FrontendPlugin< kind: 'api'; namespace: undefined; name: 'icons'; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/feature-flags': ExtensionDefinition<{ kind: 'api'; @@ -334,6 +357,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/translations': ExtensionDefinition<{ config: {}; @@ -366,6 +392,9 @@ const appPlugin: FrontendPlugin< kind: 'api'; namespace: undefined; name: 'translations'; + params: { + factory: AnyApiFactory; + }; }>; 'app-root-element:app/oauth-request-dialog': ExtensionDefinition<{ kind: 'app-root-element'; @@ -379,6 +408,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + element: JSX.Element | (() => JSX.Element); + }; }>; 'app-root-element:app/alert-display': ExtensionDefinition<{ config: { @@ -414,6 +446,9 @@ const appPlugin: FrontendPlugin< kind: 'app-root-element'; namespace: undefined; name: 'alert-display'; + params: { + element: JSX.Element | (() => JSX.Element); + }; }>; 'api:app/discovery': ExtensionDefinition<{ kind: 'api'; @@ -427,6 +462,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/alert': ExtensionDefinition<{ kind: 'api'; @@ -440,6 +478,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/analytics': ExtensionDefinition<{ kind: 'api'; @@ -453,6 +494,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/error': ExtensionDefinition<{ kind: 'api'; @@ -466,6 +510,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/storage': ExtensionDefinition<{ kind: 'api'; @@ -479,6 +526,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/fetch': ExtensionDefinition<{ kind: 'api'; @@ -492,6 +542,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/oauth-request': ExtensionDefinition<{ kind: 'api'; @@ -505,6 +558,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/google-auth': ExtensionDefinition<{ kind: 'api'; @@ -518,6 +574,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/microsoft-auth': ExtensionDefinition<{ kind: 'api'; @@ -531,6 +590,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/github-auth': ExtensionDefinition<{ kind: 'api'; @@ -544,6 +606,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/okta-auth': ExtensionDefinition<{ kind: 'api'; @@ -557,6 +622,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/gitlab-auth': ExtensionDefinition<{ kind: 'api'; @@ -570,6 +638,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/onelogin-auth': ExtensionDefinition<{ kind: 'api'; @@ -583,6 +654,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/bitbucket-auth': ExtensionDefinition<{ kind: 'api'; @@ -596,6 +670,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/bitbucket-server-auth': ExtensionDefinition<{ kind: 'api'; @@ -609,6 +686,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/atlassian-auth': ExtensionDefinition<{ kind: 'api'; @@ -622,6 +702,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/vmware-cloud-auth': ExtensionDefinition<{ kind: 'api'; @@ -635,6 +718,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:app/permission': ExtensionDefinition<{ kind: 'api'; @@ -648,6 +734,9 @@ const appPlugin: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; } >; diff --git a/plugins/catalog-graph/api-report-alpha.md b/plugins/catalog-graph/api-report-alpha.md index 65f8af3764..67b1478597 100644 --- a/plugins/catalog-graph/api-report-alpha.md +++ b/plugins/catalog-graph/api-report-alpha.md @@ -91,6 +91,10 @@ const _default: FrontendPlugin< kind: 'entity-card'; namespace: undefined; name: 'relations'; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'page:catalog-graph': ExtensionDefinition<{ config: { @@ -153,6 +157,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; } >; diff --git a/plugins/catalog-import/api-report-alpha.md b/plugins/catalog-import/api-report-alpha.md index 3eed9ce3e4..6c3e80a477 100644 --- a/plugins/catalog-import/api-report-alpha.md +++ b/plugins/catalog-import/api-report-alpha.md @@ -30,6 +30,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'page:catalog-import': ExtensionDefinition<{ kind: 'page'; @@ -56,6 +59,11 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; } >; diff --git a/plugins/catalog/api-report-alpha.md b/plugins/catalog/api-report-alpha.md index 62e631dd4a..c6b4a02871 100644 --- a/plugins/catalog/api-report-alpha.md +++ b/plugins/catalog/api-report-alpha.md @@ -20,6 +20,7 @@ import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha'; import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha'; +import { SearchResultListItemBlueprintParams } from '@backstage/plugin-search-react/alpha'; import { TranslationRef } from '@backstage/core-plugin-api/alpha'; // @alpha @@ -158,6 +159,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'nav-item:catalog': ExtensionDefinition<{ kind: 'nav-item'; @@ -175,6 +179,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; 'api:catalog/starred-entities': ExtensionDefinition<{ kind: 'api'; @@ -188,6 +197,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:catalog/entity-presentation': ExtensionDefinition<{ kind: 'api'; @@ -201,6 +213,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'entity-card:catalog/about': ExtensionDefinition<{ kind: 'entity-card'; @@ -229,6 +244,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/links': ExtensionDefinition<{ kind: 'entity-card'; @@ -257,6 +276,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/labels': ExtensionDefinition<{ kind: 'entity-card'; @@ -285,6 +308,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/depends-on-components': ExtensionDefinition<{ kind: 'entity-card'; @@ -313,6 +340,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/depends-on-resources': ExtensionDefinition<{ kind: 'entity-card'; @@ -341,6 +372,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/has-components': ExtensionDefinition<{ kind: 'entity-card'; @@ -369,6 +404,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/has-resources': ExtensionDefinition<{ kind: 'entity-card'; @@ -397,6 +436,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/has-subcomponents': ExtensionDefinition<{ kind: 'entity-card'; @@ -425,6 +468,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/has-subdomains': ExtensionDefinition<{ kind: 'entity-card'; @@ -453,6 +500,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:catalog/has-systems': ExtensionDefinition<{ kind: 'entity-card'; @@ -481,6 +532,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-content:catalog/overview': ExtensionDefinition<{ config: { @@ -548,6 +603,13 @@ const _default: FrontendPlugin< kind: 'entity-content'; namespace: undefined; name: 'overview'; + params: { + loader: () => Promise; + defaultPath: string; + defaultTitle: string; + routeRef?: RouteRef | undefined; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'catalog-filter:catalog/tag': ExtensionDefinition<{ kind: 'catalog-filter'; @@ -561,6 +623,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/kind': ExtensionDefinition<{ config: { @@ -586,6 +651,9 @@ const _default: FrontendPlugin< kind: 'catalog-filter'; namespace: undefined; name: 'kind'; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/type': ExtensionDefinition<{ kind: 'catalog-filter'; @@ -599,6 +667,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/mode': ExtensionDefinition<{ config: { @@ -624,6 +695,9 @@ const _default: FrontendPlugin< kind: 'catalog-filter'; namespace: undefined; name: 'mode'; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/namespace': ExtensionDefinition<{ kind: 'catalog-filter'; @@ -637,6 +711,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/lifecycle': ExtensionDefinition<{ kind: 'catalog-filter'; @@ -650,6 +727,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/processing-status': ExtensionDefinition<{ kind: 'catalog-filter'; @@ -663,6 +743,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + loader: () => Promise; + }; }>; 'catalog-filter:catalog/list': ExtensionDefinition<{ config: { @@ -688,6 +771,9 @@ const _default: FrontendPlugin< kind: 'catalog-filter'; namespace: undefined; name: 'list'; + params: { + loader: () => Promise; + }; }>; 'page:catalog': ExtensionDefinition<{ config: { @@ -718,6 +804,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'page:catalog/entity': ExtensionDefinition<{ config: { @@ -775,6 +866,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: 'entity'; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'search-result-list-item:catalog': ExtensionDefinition<{ kind: 'search-result-list-item'; @@ -795,6 +891,7 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: SearchResultListItemBlueprintParams; }>; } >; diff --git a/plugins/devtools/api-report-alpha.md b/plugins/devtools/api-report-alpha.md index 7fb16734f1..9a9bbbb463 100644 --- a/plugins/devtools/api-report-alpha.md +++ b/plugins/devtools/api-report-alpha.md @@ -31,6 +31,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'page:devtools': ExtensionDefinition<{ kind: 'page'; @@ -57,6 +60,11 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'nav-item:devtools': ExtensionDefinition<{ kind: 'nav-item'; @@ -74,6 +82,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; } >; diff --git a/plugins/home/api-report-alpha.md b/plugins/home/api-report-alpha.md index 7b4b157eb2..71aa85d0f5 100644 --- a/plugins/home/api-report-alpha.md +++ b/plugins/home/api-report-alpha.md @@ -62,6 +62,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; } >; diff --git a/plugins/kubernetes/api-report-alpha.md b/plugins/kubernetes/api-report-alpha.md index 852a5c6c1a..4ffdc36a96 100644 --- a/plugins/kubernetes/api-report-alpha.md +++ b/plugins/kubernetes/api-report-alpha.md @@ -33,6 +33,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'page:kubernetes': ExtensionDefinition<{ kind: 'page'; @@ -55,6 +58,11 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'entity-content:kubernetes/kubernetes': ExtensionDefinition<{ kind: 'entity-content'; @@ -100,6 +108,13 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + defaultPath: string; + defaultTitle: string; + routeRef?: RouteRef | undefined; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'api:kubernetes/proxy': ExtensionDefinition<{ kind: 'api'; @@ -113,6 +128,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:kubernetes/auth-providers': ExtensionDefinition<{ kind: 'api'; @@ -126,6 +144,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'api:kubernetes/cluster-link-formatter': ExtensionDefinition<{ kind: 'api'; @@ -139,6 +160,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; } >; diff --git a/plugins/org/api-report-alpha.md b/plugins/org/api-report-alpha.md index a8c6bd70ec..5a88d229ce 100644 --- a/plugins/org/api-report-alpha.md +++ b/plugins/org/api-report-alpha.md @@ -48,6 +48,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:org/members-list': ExtensionDefinition<{ kind: 'entity-card'; @@ -80,6 +84,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:org/ownership': ExtensionDefinition<{ kind: 'entity-card'; @@ -112,6 +120,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'entity-card:org/user-profile': ExtensionDefinition<{ kind: 'entity-card'; @@ -144,6 +156,10 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + loader: () => Promise; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; } >; diff --git a/plugins/scaffolder/api-report-alpha.md b/plugins/scaffolder/api-report-alpha.md index cd4cd7ce3c..47c041e0d8 100644 --- a/plugins/scaffolder/api-report-alpha.md +++ b/plugins/scaffolder/api-report-alpha.md @@ -56,6 +56,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'page:scaffolder': ExtensionDefinition<{ kind: 'page'; @@ -82,6 +85,11 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'nav-item:scaffolder': ExtensionDefinition<{ kind: 'nav-item'; @@ -99,6 +107,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; } >; diff --git a/plugins/search/api-report-alpha.md b/plugins/search/api-report-alpha.md index fe06873bd4..ed54b23a2f 100644 --- a/plugins/search/api-report-alpha.md +++ b/plugins/search/api-report-alpha.md @@ -34,6 +34,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'nav-item:search': ExtensionDefinition<{ kind: 'nav-item'; @@ -51,6 +54,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; 'page:search': ExtensionDefinition<{ config: { @@ -96,6 +104,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; } >; @@ -110,6 +123,9 @@ export const searchApi: ExtensionDefinition<{ configInput: {}; output: ConfigurableExtensionDataRef; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; // @alpha (undocumented) @@ -129,6 +145,11 @@ export const searchNavItem: ExtensionDefinition<{ {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; // @alpha (undocumented) @@ -172,6 +193,11 @@ export const searchPage: ExtensionDefinition<{ kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; // (No @packageDocumentation comment for this package) diff --git a/plugins/techdocs/api-report-alpha.md b/plugins/techdocs/api-report-alpha.md index dfe5dc2cc2..6be48f4331 100644 --- a/plugins/techdocs/api-report-alpha.md +++ b/plugins/techdocs/api-report-alpha.md @@ -16,6 +16,7 @@ import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha'; import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha'; +import { SearchResultListItemBlueprintParams } from '@backstage/plugin-search-react/alpha'; // @alpha (undocumented) const _default: FrontendPlugin< @@ -42,6 +43,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'page:techdocs': ExtensionDefinition<{ kind: 'page'; @@ -68,6 +72,11 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'nav-item:techdocs': ExtensionDefinition<{ kind: 'nav-item'; @@ -85,6 +94,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; 'api:techdocs/storage': ExtensionDefinition<{ kind: 'api'; @@ -98,6 +112,9 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + factory: AnyApiFactory; + }; }>; 'search-result-list-item:techdocs': ExtensionDefinition<{ config: { @@ -136,6 +153,7 @@ const _default: FrontendPlugin< kind: 'search-result-list-item'; namespace: undefined; name: undefined; + params: SearchResultListItemBlueprintParams; }>; 'page:techdocs/reader': ExtensionDefinition<{ kind: 'page'; @@ -162,6 +180,11 @@ const _default: FrontendPlugin< } >; inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; 'entity-content:techdocs': ExtensionDefinition<{ config: { @@ -223,6 +246,13 @@ const _default: FrontendPlugin< kind: 'entity-content'; namespace: undefined; name: undefined; + params: { + loader: () => Promise; + defaultPath: string; + defaultTitle: string; + routeRef?: RouteRef | undefined; + filter?: string | ((entity: Entity) => boolean) | undefined; + }; }>; 'empty-state:techdocs/entity-content': ExtensionDefinition<{ config: {}; @@ -243,6 +273,7 @@ const _default: FrontendPlugin< } >; }; + params: never; kind: 'empty-state'; namespace: undefined; name: 'entity-content'; @@ -289,6 +320,7 @@ export const techDocsSearchResultListItemExtension: ExtensionDefinition<{ kind: 'search-result-list-item'; namespace: undefined; name: undefined; + params: SearchResultListItemBlueprintParams; }>; // (No @packageDocumentation comment for this package) diff --git a/plugins/user-settings/api-report-alpha.md b/plugins/user-settings/api-report-alpha.md index 199dbba582..8dcba01f68 100644 --- a/plugins/user-settings/api-report-alpha.md +++ b/plugins/user-settings/api-report-alpha.md @@ -36,6 +36,11 @@ const _default: FrontendPlugin< {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; 'page:user-settings': ExtensionDefinition<{ config: { @@ -74,6 +79,11 @@ const _default: FrontendPlugin< kind: 'page'; namespace: undefined; name: undefined; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; }>; } >; @@ -96,6 +106,11 @@ export const settingsNavItem: ExtensionDefinition<{ {} >; inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; }>; // @alpha (undocumented) From d64f16acc547f3779debe67e9bb70eabc4897bc8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Sep 2024 12:52:01 +0200 Subject: [PATCH 6/6] docs/frontend-system: document param overrides Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 0da2ed6eef..faaf446f41 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -79,6 +79,48 @@ const myOverrideExtension = myExtension.override({ Note the `yield*` expression, which forwards all values from the provided iterable to the generator, in this case the original factory output. +## Overriding blueprint parameters + +If you are overriding an extension that was originally created from an [extension blueprint](./23-extension-blueprints.md), you are able to override the parameters that were originally provided for the blueprint. This can be done directly as an option to `.override`, or when calling the original factory in the override factory. The provided parameter overrides will be merged with the existing parameters that where provided when creating the extension from the blueprint. + +For example, consider the following extension created from the `PageBlueprint`: + +```tsx +const exampleExtension = PageBlueprint.make({ + params: { + loader: () => + import('./components/ExamplePage').then(m => ), + defaultPath: '/example', + }, +}); +``` + +You can immediately override parameters through the `params` option: + +```tsx +const overrideExtension = exampleExtension.override({ + params: { + loader: () => + import('./components/OverridePage').then(m => ), + }, +}); +``` + +It is also possible to pass parameter overrides when calling the original factory in the override factory: + +```tsx +const overrideExtension = exampleExtension.override({ + factory(originalFactory) { + return originalFactory({ + params: { + loader: () => + import('./components/OverridePage').then(m => ), + }, + }); + }, +}); +``` + ## Overriding declared outputs When overriding an extension you can provide a new output declaration. This **replaces** any existing output declaration, which means that if you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration: