From af5796f5c38a6b4de7a4bffaf5ecc97f8d9a4513 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 19 Aug 2024 14:48:25 +0200 Subject: [PATCH] frontend-plugin-api: remove support for v1 extensions Signed-off-by: Patrik Oldsberg --- .../src/wiring/createExtension.test.ts | 219 +++++++---------- .../src/wiring/createExtension.ts | 225 +++++------------- .../src/wiring/createExtensionInput.test.ts | 42 +--- .../src/wiring/createExtensionInput.ts | 78 ++---- .../wiring/createExtensionOverrides.test.ts | 16 +- .../src/wiring/createFrontendPlugin.test.ts | 77 +++--- .../frontend-plugin-api/src/wiring/index.ts | 5 - .../src/wiring/resolveExtensionDefinition.ts | 27 ++- 8 files changed, 236 insertions(+), 453 deletions(-) diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 5902c5c3e7..5853f28004 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -21,6 +21,9 @@ import { createExtensionInput } from './createExtensionInput'; const stringDataRef = createExtensionDataRef().with({ id: 'string' }); const numberDataRef = createExtensionDataRef().with({ id: 'number' }); +const booleanDataRef = createExtensionDataRef().with({ + id: 'boolean', +}); function unused(..._any: any[]) {} @@ -29,42 +32,35 @@ describe('createExtension', () => { const baseConfig = { namespace: 'test', attachTo: { id: 'root', input: 'default' }, - output: { - foo: stringDataRef, - }, + output: [stringDataRef], }; const extension = createExtension({ ...baseConfig, factory() { - return { - foo: 'bar', - }; + return [stringDataRef('bar')]; }, }); - expect(extension).toMatchObject({ version: 'v1', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); - // When declared as an error function without a block the TypeScript errors - // are a more specific and will often point at the property that is problematic. + // Member arrow function declaration + createExtension({ + ...baseConfig, + factory: () => [ + stringDataRef( + // @ts-expect-error + 3, + ), + ], + }); // @ts-expect-error createExtension({ ...baseConfig, - factory: () => ({ - foo: 3, - }), + factory: () => [numberDataRef(3)], }); + // @ts-expect-error createExtension({ ...baseConfig, - factory: () => - // @ts-expect-error - ({ - bar: 'bar', - }), - }); - createExtension({ - ...baseConfig, - factory: () => - // @ts-expect-error - ({}), + factory: () => [], }); createExtension({ ...baseConfig, @@ -79,39 +75,37 @@ describe('createExtension', () => { 'bar', }); - // When declared as a function with a block the TypeScript error will instead - // be tied to the factory function declaration itself, but the error messages - // is still helpful and points to part of the return type that is problematic. + // Method declaration createExtension({ ...baseConfig, - // @ts-expect-error factory() { - return { - foo: 3, - }; + return [ + stringDataRef( + // @ts-expect-error + 3, + ), + ]; + }, + }); + // @ts-expect-error + createExtension({ + ...baseConfig, + factory() { + return [numberDataRef(3)]; + }, + }); + // @ts-expect-error + createExtension({ + ...baseConfig, + factory() { + return []; }, }); createExtension({ ...baseConfig, // @ts-expect-error factory() { - return { - bar: 'bar', - }; - }, - }); - createExtension({ - ...baseConfig, - // @ts-expect-error - factory() { - return {}; - }, - }); - createExtension({ - ...baseConfig, - // @ts-expect-error - factory() { - return {}; + return; }, }); createExtension({ @@ -122,36 +116,37 @@ describe('createExtension', () => { }, }); + // Member function declaration createExtension({ ...baseConfig, - // @ts-expect-error factory: () => { - return { - foo: 3, - }; + return [ + stringDataRef( + // @ts-expect-error + 3, + ), + ]; + }, + }); + // @ts-expect-error + createExtension({ + ...baseConfig, + factory: () => { + return [numberDataRef(3)]; + }, + }); + // @ts-expect-error + createExtension({ + ...baseConfig, + factory: () => { + return []; }, }); createExtension({ ...baseConfig, // @ts-expect-error factory: () => { - return { - bar: 'bar', - }; - }, - }); - createExtension({ - ...baseConfig, - // @ts-expect-error - factory: () => { - return {}; - }, - }); - createExtension({ - ...baseConfig, - // @ts-expect-error - factory: () => { - return {}; + return; }, }); createExtension({ @@ -167,52 +162,27 @@ describe('createExtension', () => { const baseConfig = { namespace: 'test', attachTo: { id: 'root', input: 'default' }, - output: { - foo: stringDataRef, - bar: stringDataRef.optional(), - }, + output: [stringDataRef, numberDataRef.optional()], }; const extension = createExtension({ ...baseConfig, - factory: () => ({ - foo: 'bar', - }), + factory: () => [stringDataRef('bar')], }); - expect(extension).toMatchObject({ version: 'v1', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); createExtension({ ...baseConfig, - factory: () => ({ - foo: 'bar', - bar: 'baz', - }), + factory: () => [stringDataRef('bar'), numberDataRef(3)], }); // @ts-expect-error createExtension({ ...baseConfig, - factory: () => ({ - foo: 3, - }), + factory: () => [numberDataRef(3)], }); // @ts-expect-error createExtension({ ...baseConfig, - factory: () => ({ - foo: 'bar', - bar: 3, - }), - }); - createExtension({ - ...baseConfig, - factory: () => - // @ts-expect-error - ({ bar: 'bar' }), - }); - createExtension({ - ...baseConfig, - factory: () => - // @ts-expect-error - ({}), + factory: () => [], }); createExtension({ ...baseConfig, @@ -238,57 +208,48 @@ describe('createExtension', () => { namespace: 'test', attachTo: { id: 'root', input: 'default' }, inputs: { - mixed: createExtensionInput({ - required: stringDataRef, - optional: stringDataRef.optional(), - }), - onlyRequired: createExtensionInput({ - required: stringDataRef, - }), - onlyOptional: createExtensionInput({ - optional: stringDataRef.optional(), - }), - }, - output: { - foo: stringDataRef, + mixed: createExtensionInput([stringDataRef, numberDataRef.optional()]), + onlyRequired: createExtensionInput([stringDataRef]), + onlyOptional: createExtensionInput([stringDataRef.optional()]), }, + output: [stringDataRef], factory({ inputs }) { - const a1: string = inputs.mixed?.[0].output.required; + const a1: string = inputs.mixed?.[0].get(stringDataRef); // @ts-expect-error - const a2: number = inputs.mixed?.[0].output.required; + const a2: number = inputs.mixed?.[0].get(stringDataRef); // @ts-expect-error - const a3: any = inputs.mixed?.[0].output.nonExistent; + const a3: any = inputs.mixed?.[0].get(booleanDataRef); unused(a1, a2, a3); - const b1: string | undefined = inputs.mixed?.[0].output.optional; + const b1: number | undefined = inputs.mixed?.[0].get(numberDataRef); // @ts-expect-error - const b2: string = inputs.mixed?.[0].output.optional; + const b2: string = inputs.mixed?.[0].get(numberDataRef); // @ts-expect-error - const b3: number = inputs.mixed?.[0].output.optional; + const b3: number = inputs.mixed?.[0].get(numberDataRef); // @ts-expect-error - const b4: number | undefined = inputs.mixed?.[0].output.optional; + const b4: string | undefined = inputs.mixed?.[0].get(numberDataRef); unused(b1, b2, b3, b4); - const c1: string = inputs.onlyRequired?.[0].output.required; + const c1: string = inputs.onlyRequired?.[0].get(stringDataRef); // @ts-expect-error - const c2: number = inputs.onlyRequired?.[0].output.required; + const c2: number = inputs.onlyRequired?.[0].get(stringDataRef); unused(c1, c2); - const d1: string | undefined = inputs.onlyOptional?.[0].output.optional; + const d1: string | undefined = + inputs.onlyOptional?.[0].get(stringDataRef); // @ts-expect-error - const d2: string = inputs.onlyOptional?.[0].output.optional; + const d2: string = inputs.onlyOptional?.[0].get(stringDataRef); // @ts-expect-error - const d3: number = inputs.onlyOptional?.[0].output.optional; + const d3: number = inputs.onlyOptional?.[0].get(stringDataRef); // @ts-expect-error - const d4: number | undefined = inputs.onlyOptional?.[0].output.optional; + const d4: number | undefined = + inputs.onlyOptional?.[0].get(stringDataRef); unused(d1, d2, d3, d4); - return { - foo: 'bar', - }; + return [stringDataRef('bar')]; }, }); - expect(extension).toMatchObject({ version: 'v1', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); expect(String(extension)).toBe( 'ExtensionDefinition{namespace=test,attachTo=root@default}', ); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 66b908f236..0c89ff1567 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -15,7 +15,7 @@ */ import { AppNode } from '../apis'; -import { PortableSchema, createSchemaFromZod } from '../schema'; +import { PortableSchema } from '../schema'; import { Expand } from '../types'; import { ResolveInputValueOverrides, @@ -29,46 +29,9 @@ import { AnyExtensionDataRef, ExtensionDataValue, } from './createExtensionDataRef'; -import { ExtensionInput, LegacyExtensionInput } from './createExtensionInput'; +import { ExtensionInput } from './createExtensionInput'; import { z } from 'zod'; - -/** - * @public - * @deprecated Extension data maps will be removed. - */ -export type AnyExtensionDataMap = { - [name in string]: AnyExtensionDataRef; -}; - -/** - * @public - * @deprecated This type will be removed. - */ -export type AnyExtensionInputMap = { - [inputName in string]: LegacyExtensionInput< - AnyExtensionDataMap, - { optional: boolean; singleton: boolean } - >; -}; - -/** - * Converts an extension data map into the matching concrete data values type. - * @public - * @deprecated Extension data maps will be removed. - */ -export type ExtensionDataValues = { - [DataName in keyof TExtensionData as TExtensionData[DataName]['config'] extends { - optional: true; - } - ? never - : DataName]: TExtensionData[DataName]['T']; -} & { - [DataName in keyof TExtensionData as TExtensionData[DataName]['config'] extends { - optional: true; - } - ? DataName - : never]?: TExtensionData[DataName]['T']; -}; +import { createSchemaFromZod } from '../schema/createSchemaFromZod'; /** * Convert a single extension input into a matching resolved input. @@ -80,11 +43,6 @@ export type ResolvedExtensionInput< ? { node: AppNode; } & ExtensionDataContainer - : TExtensionInput['extensionData'] extends AnyExtensionDataMap - ? { - node: AppNode; - output: ExtensionDataValues; - } : never; /** @@ -93,7 +51,7 @@ export type ResolvedExtensionInput< */ export type ResolvedExtensionInputs< TInputs extends { - [name in string]: ExtensionInput | LegacyExtensionInput; + [name in string]: ExtensionInput; }, > = { [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] @@ -103,31 +61,6 @@ export type ResolvedExtensionInputs< : Expand | undefined>; }; -/** - * @public - * @deprecated This way of structuring the options is deprecated, this type will be removed in the future - */ -export interface LegacyCreateExtensionOptions< - TOutput extends AnyExtensionDataMap, - TInputs extends AnyExtensionInputMap, - TConfig, - TConfigInput, -> { - kind?: string; - namespace?: string; - name?: string; - attachTo: { id: string; input: string }; - disabled?: boolean; - inputs?: TInputs; - output: TOutput; - configSchema?: PortableSchema; - factory(context: { - node: AppNode; - config: TConfig; - inputs: Expand>; - }): Expand>; -} - type ToIntersection = (U extends any ? (k: U) => void : never) extends ( k: infer I, ) => void @@ -326,13 +259,27 @@ export type InternalExtensionDefinition< ( | { readonly version: 'v1'; - readonly inputs: AnyExtensionInputMap; - readonly output: AnyExtensionDataMap; + readonly inputs: { + [inputName in string]: { + $$type: '@backstage/ExtensionInput'; + extensionData: { + [name in string]: AnyExtensionDataRef; + }; + config: { optional: boolean; singleton: boolean }; + }; + }; + readonly output: { + [name in string]: AnyExtensionDataRef; + }; factory(context: { node: AppNode; config: TConfig; - inputs: ResolvedExtensionInputs; - }): ExtensionDataValues; + inputs: { + [inputName in string]: unknown; + }; + }): { + [inputName in string]: unknown; + }; } | { readonly version: 'v2'; @@ -419,23 +366,6 @@ export function createExtension< name: string | undefined extends TName ? undefined : TName; } >; -/** - * @public - * @deprecated - use the array format of `output` instead, see TODO-doc-link - */ -export function createExtension< - TOutput extends AnyExtensionDataMap, - TInputs extends AnyExtensionInputMap, - TConfig, - TConfigInput, ->( - options: LegacyCreateExtensionOptions< - TOutput, - TInputs, - TConfig, - TConfigInput - >, -): ExtensionDefinition; export function createExtension< const TKind extends string | undefined, const TNamespace extends string | undefined, @@ -447,43 +377,31 @@ export function createExtension< { optional: boolean; singleton: boolean } >; }, - TLegacyInputs extends AnyExtensionInputMap, - TConfig, - TConfigInput, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, UFactoryOutput extends ExtensionDataValue, >( - options: - | CreateExtensionOptions< - TKind, - TNamespace, - TName, - UOutput, - TInputs, - TConfigSchema, - UFactoryOutput - > - | LegacyCreateExtensionOptions< - AnyExtensionDataMap, - TLegacyInputs, - TConfig, - TConfigInput - >, + options: CreateExtensionOptions< + TKind, + TNamespace, + TName, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput + >, ): ExtensionDefinition< - TConfig & - (string extends keyof TConfigSchema - ? {} - : { - [key in keyof TConfigSchema]: z.infer>; - }), - TConfigInput & - (string extends keyof TConfigSchema - ? {} - : z.input< - z.ZodObject<{ - [key in keyof TConfigSchema]: ReturnType; - }> - >), + string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }, + string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >, UOutput, TInputs, { @@ -492,29 +410,20 @@ export function createExtension< name: TName; } > { - if ('configSchema' in options && 'config' in options) { - throw new Error(`Cannot provide both configSchema and config.schema`); - } - let configSchema: PortableSchema | undefined; - if ('configSchema' in options) { - configSchema = options.configSchema; - } - if ('config' in options) { - const newConfigSchema = options.config?.schema; - configSchema = - newConfigSchema && - createSchemaFromZod(innerZ => - innerZ.object( - Object.fromEntries( - Object.entries(newConfigSchema).map(([k, v]) => [k, v(innerZ)]), - ), + const schemaDeclaration = options.config?.schema; + const configSchema = + schemaDeclaration && + createSchemaFromZod(innerZ => + innerZ.object( + Object.fromEntries( + Object.entries(schemaDeclaration).map(([k, v]) => [k, v(innerZ)]), ), - ); - } + ), + ); return { $$type: '@backstage/ExtensionDefinition', - version: Symbol.iterator in options.output ? 'v2' : 'v1', + version: 'v2', kind: options.kind, namespace: options.namespace, name: options.name, @@ -687,22 +596,18 @@ export function createExtension< } as CreateExtensionOptions); }, } as InternalExtensionDefinition< - TConfig & - (string extends keyof TConfigSchema - ? {} - : { - [key in keyof TConfigSchema]: z.infer< - ReturnType - >; - }), - TConfigInput & - (string extends keyof TConfigSchema - ? {} - : z.input< - z.ZodObject<{ - [key in keyof TConfigSchema]: ReturnType; - }> - >), + string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }, + string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >, UOutput, TInputs, { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts index e401adc675..084758b793 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts @@ -15,11 +15,7 @@ */ import { createExtensionDataRef } from './createExtensionDataRef'; -import { - ExtensionInput, - LegacyExtensionInput, - createExtensionInput, -} from './createExtensionInput'; +import { ExtensionInput, createExtensionInput } from './createExtensionInput'; const stringDataRef = createExtensionDataRef().with({ id: 'str' }); const numberDataRef = createExtensionDataRef().with({ id: 'num' }); @@ -130,40 +126,4 @@ describe('createExtensionInput', () => { createExtensionInput([stringDataRef, stringDataRef], { singleton: true }), ).toThrow("ExtensionInput may not have duplicate data refs: 'str'"); }); - - describe('old api', () => { - it('should create a regular input', () => { - const input = createExtensionInput({ - str: stringDataRef, - num: numberDataRef, - }); - expect(input).toEqual({ - $$type: '@backstage/ExtensionInput', - extensionData: { str: stringDataRef, num: numberDataRef }, - config: { singleton: false, optional: false }, - }); - - const x1: LegacyExtensionInput< - { str: typeof stringDataRef; num: typeof numberDataRef }, - { singleton: false; optional: false } - > = input; - // @ts-expect-error - const x2: LegacyExtensionInput< - { str: typeof numberDataRef; num: typeof stringDataRef }, // switched - { singleton: false; optional: false } - > = input; - // @ts-expect-error - const x3: LegacyExtensionInput< - { str: typeof stringDataRef; num: typeof numberDataRef }, - { singleton: true; optional: false } - > = input; - // @ts-expect-error - const x4: LegacyExtensionInput< - { str: typeof stringDataRef; num: typeof numberDataRef }, - { singleton: false; optional: true } - > = input; - - unused(x1, x2, x3, x4); - }); - }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts index b7efb13e6e..3651eb3ee5 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { AnyExtensionDataMap } from './createExtension'; import { ExtensionDataRef } from './createExtensionDataRef'; /** @public */ @@ -27,36 +26,6 @@ export interface ExtensionInput< config: TConfig; } -/** - * @public - * @deprecated This type will be removed. Use `ExtensionInput` instead. - */ -export interface LegacyExtensionInput< - TExtensionDataMap extends AnyExtensionDataMap, - TConfig extends { singleton: boolean; optional: boolean }, -> { - $$type: '@backstage/ExtensionInput'; - extensionData: TExtensionDataMap; - config: TConfig; -} - -/** - * @public - * @deprecated Use the following form instead: `createExtensionInput([dataRef1, dataRef2])` - */ -export function createExtensionInput< - TExtensionDataMap extends AnyExtensionDataMap, - TConfig extends { singleton?: boolean; optional?: boolean }, ->( - extensionData: TExtensionDataMap, - config?: TConfig, -): LegacyExtensionInput< - TExtensionDataMap, - { - singleton: TConfig['singleton'] extends true ? true : false; - optional: TConfig['optional'] extends true ? true : false; - } ->; /** @public */ export function createExtensionInput< UExtensionData extends ExtensionDataRef, @@ -73,26 +42,17 @@ export function createExtensionInput< >; export function createExtensionInput< TExtensionData extends ExtensionDataRef, - TExtensionDataMap extends AnyExtensionDataMap, TConfig extends { singleton?: boolean; optional?: boolean }, >( - extensionData: Array | TExtensionDataMap, + extensionData: Array, config?: TConfig, -): - | LegacyExtensionInput< - TExtensionDataMap, - { - singleton: TConfig['singleton'] extends true ? true : false; - optional: TConfig['optional'] extends true ? true : false; - } - > - | ExtensionInput< - TExtensionData, - { - singleton: TConfig['singleton'] extends true ? true : false; - optional: TConfig['optional'] extends true ? true : false; - } - > { +): ExtensionInput< + TExtensionData, + { + singleton: TConfig['singleton'] extends true ? true : false; + optional: TConfig['optional'] extends true ? true : false; + } +> { if (process.env.NODE_ENV !== 'production') { if (Array.isArray(extensionData)) { const seen = new Set(); @@ -124,19 +84,11 @@ export function createExtensionInput< ? true : false, }, - } as - | LegacyExtensionInput< - TExtensionDataMap, - { - singleton: TConfig['singleton'] extends true ? true : false; - optional: TConfig['optional'] extends true ? true : false; - } - > - | ExtensionInput< - TExtensionData, - { - singleton: TConfig['singleton'] extends true ? true : false; - optional: TConfig['optional'] extends true ? true : false; - } - >; + } as ExtensionInput< + TExtensionData, + { + singleton: TConfig['singleton'] extends true ? true : false; + optional: TConfig['optional'] extends true ? true : false; + } + >; } diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts index aa00247222..ef95f82e25 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts @@ -40,22 +40,22 @@ describe('createExtensionOverrides', () => { createExtension({ name: 'a', attachTo: { id: 'app', input: 'apis' }, - output: {}, - factory: () => ({}), + output: [], + factory: () => [], }), createExtension({ namespace: 'b', attachTo: { id: 'app', input: 'apis' }, - output: {}, - factory: () => ({}), + output: [], + factory: () => [], }), createExtension({ kind: 'k', namespace: 'c', name: 'n', attachTo: { id: 'app', input: 'apis' }, - output: {}, - factory: () => ({}), + output: [], + factory: () => [], }), ], }), @@ -122,8 +122,8 @@ describe('createExtensionOverrides', () => { createExtension({ namespace: 'a', attachTo: { id: 'app', input: 'apis' }, - output: {}, - factory: () => ({}), + output: [], + factory: () => [], }), ], }); diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts index 71508b0c15..3615ba29e5 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts @@ -17,7 +17,6 @@ import React from 'react'; import { createApp } from '@backstage/frontend-app-api'; import { screen } from '@testing-library/react'; -import { createSchemaFromZod } from '../schema/createSchemaFromZod'; import { createFrontendPlugin } from './createFrontendPlugin'; import { JsonObject } from '@backstage/types'; import { createExtension } from './createExtension'; @@ -43,14 +42,14 @@ const Extension1 = createExtension({ const Extension2 = createExtension({ name: '2', attachTo: { id: 'test/output', input: 'names' }, - output: { - name: nameExtensionDataRef, + output: [nameExtensionDataRef], + config: { + schema: { + name: z => z.string().default('extension-2'), + }, }, - configSchema: createSchemaFromZod(z => - z.object({ name: z.string().default('extension-2') }), - ), factory({ config }) { - return { name: config.name }; + return [nameExtensionDataRef(config.name)]; }, }); @@ -58,45 +57,45 @@ const Extension3 = createExtension({ name: '3', attachTo: { id: 'test/output', input: 'names' }, inputs: { - addons: createExtensionInput({ - name: nameExtensionDataRef, - }), - }, - output: { - name: nameExtensionDataRef, + addons: createExtensionInput([nameExtensionDataRef]), }, + output: [nameExtensionDataRef], factory({ inputs }) { - return { - name: `extension-3:${inputs.addons.map(n => n.output.name).join('-')}`, - }; + return [ + nameExtensionDataRef( + `extension-3:${inputs.addons + .map(n => n.get(nameExtensionDataRef)) + .join('-')}`, + ), + ]; }, }); const Child = createExtension({ name: 'child', attachTo: { id: 'test/3', input: 'addons' }, - output: { - name: nameExtensionDataRef, + output: [nameExtensionDataRef], + config: { + schema: { + name: z => z.string().default('child'), + }, }, - configSchema: createSchemaFromZod(z => - z.object({ name: z.string().default('child') }), - ), factory({ config }) { - return { name: config.name }; + return [nameExtensionDataRef(config.name)]; }, }); const Child2 = createExtension({ name: 'child2', attachTo: { id: 'test/3', input: 'addons' }, - output: { - name: nameExtensionDataRef, + output: [nameExtensionDataRef], + config: { + schema: { + name: z => z.string().default('child2'), + }, }, - configSchema: createSchemaFromZod(z => - z.object({ name: z.string().default('child2') }), - ), factory({ config }) { - return { name: config.name }; + return [nameExtensionDataRef(config.name)]; }, }); @@ -104,19 +103,19 @@ const outputExtension = createExtension({ name: 'output', attachTo: { id: 'app', input: 'root' }, inputs: { - names: createExtensionInput({ - name: nameExtensionDataRef, - }), - }, - output: { - element: coreExtensionData.reactElement, + names: createExtensionInput([nameExtensionDataRef]), }, + output: [coreExtensionData.reactElement], factory({ inputs }) { - return { - element: React.createElement('span', {}, [ - `Names: ${inputs.names.map(n => n.output.name).join(', ')}`, - ]), - }; + return [ + coreExtensionData.reactElement( + React.createElement('span', {}, [ + `Names: ${inputs.names + .map(n => n.get(nameExtensionDataRef)) + .join(', ')}`, + ]), + ), + ]; }, }); diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 0b64490742..3862c0e178 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -19,17 +19,12 @@ export { createExtension, type ExtensionDefinition, type CreateExtensionOptions, - type ExtensionDataValues, type ResolvedExtensionInput, type ResolvedExtensionInputs, - type LegacyCreateExtensionOptions, - type AnyExtensionInputMap, - type AnyExtensionDataMap, } from './createExtension'; export { createExtensionInput, type ExtensionInput, - type LegacyExtensionInput, } from './createExtensionInput'; export { type ExtensionDataContainer } from './createExtensionDataContainer'; export { diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index 9cd4a76882..3cbc28f8ff 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -16,9 +16,6 @@ import { AppNode } from '../apis'; import { - AnyExtensionDataMap, - AnyExtensionInputMap, - ExtensionDataValues, ExtensionDefinition, ResolvedExtensionInputs, toInternalExtensionDefinition, @@ -47,13 +44,27 @@ export type InternalExtension = Extension< ( | { readonly version: 'v1'; - readonly inputs: AnyExtensionInputMap; - readonly output: AnyExtensionDataMap; - factory(options: { + readonly inputs: { + [inputName in string]: { + $$type: '@backstage/ExtensionInput'; + extensionData: { + [name in string]: AnyExtensionDataRef; + }; + config: { optional: boolean; singleton: boolean }; + }; + }; + readonly output: { + [name in string]: AnyExtensionDataRef; + }; + factory(context: { node: AppNode; config: TConfig; - inputs: ResolvedExtensionInputs; - }): ExtensionDataValues; + inputs: { + [inputName in string]: unknown; + }; + }): { + [inputName in string]: unknown; + }; } | { readonly version: 'v2';