diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index a3be782fc8..4ec1ed050d 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -19,12 +19,14 @@ import { PortableSchema, createSchemaFromZod } from '../schema'; import { Expand } from '../types'; import { ResolveInputValueOverrides, - createDataContainer, resolveInputOverrides, } from './createExtensionBlueprint'; +import { + ExtensionDataContainer, + createExtensionDataContainer, +} from './createExtensionDataContainer'; import { AnyExtensionDataRef, - ExtensionDataRef, ExtensionDataValue, } from './createExtensionDataRef'; import { ExtensionInput, LegacyExtensionInput } from './createExtensionInput'; @@ -68,28 +70,6 @@ export type ExtensionDataValues = { : never]?: TExtensionData[DataName]['T']; }; -/** @public */ -export type ExtensionDataContainer = - Iterable< - UExtensionData extends ExtensionDataRef< - infer IData, - infer IId, - infer IConfig - > - ? IConfig['optional'] extends true - ? never - : ExtensionDataValue - : never - > & { - get( - ref: ExtensionDataRef, - ): UExtensionData extends ExtensionDataRef - ? IConfig['optional'] extends true - ? IData | undefined - : IData - : never; - }; - /** * Convert a single extension input into a matching resolved input. * @public @@ -669,7 +649,7 @@ export function createExtension< }; inputs?: ResolveInputValueOverrides; }): ExtensionDataContainer => { - return createDataContainer( + return createExtensionDataContainer( newOptions.factory({ node, config: innerContext?.config ?? config, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index 57b0629859..f2f17079ca 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -16,10 +16,7 @@ import React from 'react'; import { coreExtensionData } from './coreExtensionData'; -import { - createDataContainer, - createExtensionBlueprint, -} from './createExtensionBlueprint'; +import { createExtensionBlueprint } from './createExtensionBlueprint'; import { createExtensionTester } from '@backstage/frontend-test-utils'; import { ExtensionDataValue, @@ -31,6 +28,7 @@ import { ExtensionDefinition, toInternalExtensionDefinition, } from './createExtension'; +import { createExtensionDataContainer } from './createExtensionDataContainer'; function unused(..._any: any[]) {} @@ -376,7 +374,7 @@ describe('createExtensionBlueprint', () => { }); const mockInput = (node: string, ...data: ExtensionDataValue[]) => - Object.assign(createDataContainer(data), { + Object.assign(createExtensionDataContainer(data), { node, }); const mockParentInputs = { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 01f1fa09aa..78a2b5283d 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -18,7 +18,6 @@ import { AppNode } from '../apis'; import { Expand } from '../types'; import { CreateExtensionOptions, - ExtensionDataContainer, ExtensionDefinition, ResolvedExtensionInput, ResolvedExtensionInputs, @@ -29,10 +28,13 @@ import { z } from 'zod'; import { ExtensionInput } from './createExtensionInput'; import { AnyExtensionDataRef, - ExtensionDataRef, ExtensionDataRefToValue, ExtensionDataValue, } from './createExtensionDataRef'; +import { + ExtensionDataContainer, + createExtensionDataContainer, +} from './createExtensionDataContainer'; /** * @public @@ -252,51 +254,6 @@ export interface ExtensionBlueprint< >; } -/** @internal */ -export function createDataContainer( - values: Iterable< - UData extends ExtensionDataRef - ? ExtensionDataValue - : never - >, - declaredRefs?: ExtensionDataRef[], -): ExtensionDataContainer { - const container = new Map>(); - const verifyRefs = - declaredRefs && new Map(declaredRefs.map(ref => [ref.id, ref])); - - for (const output of values) { - if (verifyRefs) { - if (!verifyRefs.delete(output.id)) { - throw new Error( - `extension data '${output.id}' was provided but not declared`, - ); - } - } - container.set(output.id, output); - } - - const remainingRefs = - verifyRefs && - Array.from(verifyRefs.values()).filter(ref => !ref.config.optional); - if (remainingRefs && remainingRefs.length > 0) { - throw new Error( - `missing required extension data value(s) '${remainingRefs - .map(ref => ref.id) - .join(', ')}'`, - ); - } - - return { - get(ref) { - return container.get(ref.id)?.value; - }, - [Symbol.iterator]() { - return container.values(); - }, - } as ExtensionDataContainer; -} - function expectArray(value: T | T[]): T[] { return value as T[]; } @@ -333,7 +290,7 @@ export function resolveInputOverrides( if (declaredInput.config.singleton) { const originalInput = expectItem(inputs[name]); if (providedData) { - const providedContainer = createDataContainer( + const providedContainer = createExtensionDataContainer( providedData as Iterable>, declaredInput.extensionData, ); @@ -362,7 +319,7 @@ export function resolveInputOverrides( ); } newInputs[name] = providedData.map((data, i) => { - const providedContainer = createDataContainer( + const providedContainer = createExtensionDataContainer( data as Iterable>, declaredInput.extensionData, ); @@ -506,7 +463,7 @@ class ExtensionBlueprintImpl< inputs?: ResolveInputValueOverrides; }, ): ExtensionDataContainer => { - return createDataContainer( + return createExtensionDataContainer( this.options.factory(innerParams, { node, config: innerContext?.config ?? config, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionDataContainer.ts b/packages/frontend-plugin-api/src/wiring/createExtensionDataContainer.ts new file mode 100644 index 0000000000..d79ec70f2f --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/createExtensionDataContainer.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AnyExtensionDataRef, + ExtensionDataRef, + ExtensionDataValue, +} from './createExtensionDataRef'; + +/** @public */ +export type ExtensionDataContainer = + Iterable< + UExtensionData extends ExtensionDataRef< + infer IData, + infer IId, + infer IConfig + > + ? IConfig['optional'] extends true + ? never + : ExtensionDataValue + : never + > & { + get( + ref: ExtensionDataRef, + ): UExtensionData extends ExtensionDataRef + ? IConfig['optional'] extends true + ? IData | undefined + : IData + : never; + }; + +/** @internal */ +export function createExtensionDataContainer( + values: Iterable< + UData extends ExtensionDataRef + ? ExtensionDataValue + : never + >, + declaredRefs?: ExtensionDataRef[], +): ExtensionDataContainer { + const container = new Map>(); + const verifyRefs = + declaredRefs && new Map(declaredRefs.map(ref => [ref.id, ref])); + + for (const output of values) { + if (verifyRefs) { + if (!verifyRefs.delete(output.id)) { + throw new Error( + `extension data '${output.id}' was provided but not declared`, + ); + } + } + container.set(output.id, output); + } + + const remainingRefs = + verifyRefs && + Array.from(verifyRefs.values()).filter(ref => !ref.config.optional); + if (remainingRefs && remainingRefs.length > 0) { + throw new Error( + `missing required extension data value(s) '${remainingRefs + .map(ref => ref.id) + .join(', ')}'`, + ); + } + + return { + get(ref) { + return container.get(ref.id)?.value; + }, + [Symbol.iterator]() { + return container.values(); + }, + } as ExtensionDataContainer; +} diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index a28069da88..5e56e5a2d7 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -17,7 +17,6 @@ export { coreExtensionData } from './coreExtensionData'; export { createExtension, - type ExtensionDataContainer, type ExtensionDefinition, type CreateExtensionOptions, type ExtensionDataValues, @@ -32,6 +31,7 @@ export { type ExtensionInput, type LegacyExtensionInput, } from './createExtensionInput'; +export { type ExtensionDataContainer } from './createExtensionDataContainer'; export { createExtensionDataRef, type AnyExtensionDataRef,