Merge pull request #25976 from backstage/rugvip/inputs

frontend-plugin-api: add support for overriding inputs values
This commit is contained in:
Patrik Oldsberg
2024-08-12 10:39:09 +02:00
committed by GitHub
7 changed files with 852 additions and 15 deletions
@@ -92,6 +92,16 @@ function resolveInputDataContainer(
get(ref) {
return dataMap.get(ref.id);
},
*[Symbol.iterator]() {
for (const [id, value] of dataMap) {
// TODO: Would be better to be able to create a new instance using the ref here instead
yield {
$$type: '@backstage/ExtensionDataValue',
id,
value,
};
}
},
} as { node: AppNode } & ExtensionDataContainer<AnyExtensionDataRef>;
}
+69 -2
View File
@@ -1281,7 +1281,7 @@ export interface ExtensionBlueprint<
params: TParams,
context?: {
config?: TConfig;
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
},
) => ExtensionDataContainer<UOutput>,
context: {
@@ -1470,7 +1470,7 @@ export interface ExtensionDefinition<
factory(
originalFactory: (context?: {
config?: TConfig;
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
}) => ExtensionDataContainer<UOutput>,
context: {
node: AppNode;
@@ -1868,6 +1868,73 @@ export type ResolvedExtensionInputs<
: Expand<ResolvedExtensionInput<TInputs[InputName]> | undefined>;
};
// @public (undocumented)
export type ResolveInputValueOverrides<
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{
optional: boolean;
singleton: boolean;
}
>;
} = {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{
optional: boolean;
singleton: boolean;
}
>;
},
> = Expand<
{
[KName in keyof TInputs as TInputs[KName] extends ExtensionInput<
any,
{
optional: infer IOptional extends boolean;
singleton: boolean;
}
>
? IOptional extends true
? never
: KName
: never]: TInputs[KName] extends ExtensionInput<
infer IDataRefs,
{
optional: boolean;
singleton: infer ISingleton extends boolean;
}
>
? ISingleton extends true
? Iterable<ExtensionDataRefToValue<IDataRefs>>
: Array<Iterable<ExtensionDataRefToValue<IDataRefs>>>
: never;
} & {
[KName in keyof TInputs as TInputs[KName] extends ExtensionInput<
any,
{
optional: infer IOptional extends boolean;
singleton: boolean;
}
>
? IOptional extends true
? KName
: never
: never]?: TInputs[KName] extends ExtensionInput<
infer IDataRefs,
{
optional: boolean;
singleton: infer ISingleton extends boolean;
}
>
? ISingleton extends true
? Iterable<ExtensionDataRefToValue<IDataRefs>>
: Array<Iterable<ExtensionDataRefToValue<IDataRefs>>>
: never;
}
>;
// @public
export type RouteFunc<TParams extends AnyRouteRefParams> = (
...[params]: TParams extends undefined
@@ -732,5 +732,297 @@ describe('createExtension', () => {
}).data(stringDataRef),
).toBe('foo-hello-override-world');
});
it('should be able to override input values', () => {
const outputRef = createExtensionDataRef<unknown>().with({
id: 'output',
});
const testDataRef1 = createExtensionDataRef<string>().with({
id: 'test1',
});
const testDataRef2 = createExtensionDataRef<string>().with({
id: 'test2',
});
const subject = createExtension({
name: 'subject',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
opt: createExtensionInput([testDataRef1.optional()], {
singleton: true,
optional: true,
}),
single: createExtensionInput(
[testDataRef1, testDataRef2.optional()],
{
singleton: true,
},
),
multi: createExtensionInput([testDataRef1]),
},
output: [outputRef],
factory({ inputs }) {
return [
outputRef({
opt: inputs.opt?.get(testDataRef1) ?? 'none',
single: inputs.single.get(testDataRef1),
singleOpt: inputs.single.get(testDataRef2) ?? 'none',
multi: inputs.multi.map(i => i.get(testDataRef1)).join(','),
}),
];
},
});
const optExt = createExtension({
name: 'opt',
attachTo: { id: 'subject', input: 'opt' },
output: [testDataRef1],
factory: () => [testDataRef1('orig-opt')],
});
const singleExt = createExtension({
name: 'single',
attachTo: { id: 'subject', input: 'single' },
output: [testDataRef1, testDataRef2.optional()],
factory: () => [testDataRef1('orig-single')],
});
const multi1Ext = createExtension({
name: 'multi1',
attachTo: { id: 'subject', input: 'multi' },
output: [testDataRef1],
factory: () => [testDataRef1('orig-multi1')],
});
const multi2Ext = createExtension({
name: 'multi2',
attachTo: { id: 'subject', input: 'multi' },
output: [testDataRef1],
factory: () => [testDataRef1('orig-multi2')],
});
expect(
createExtensionTester(subject)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
});
// All values provided
expect(
createExtensionTester(
subject.override({
factory(originalFactory) {
return originalFactory({
inputs: {
opt: [testDataRef1('opt')],
single: [testDataRef1('single'), testDataRef2('singleOpt')],
multi: [[testDataRef1('multi1')], [testDataRef1('multi2')]],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'opt',
single: 'single',
singleOpt: 'singleOpt',
multi: 'multi1,multi2',
});
// Minimal values provided
expect(
createExtensionTester(
subject.override({
factory(originalFactory) {
return originalFactory({
inputs: {
single: [testDataRef1('single')],
multi: [],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'none',
single: 'single',
singleOpt: 'none',
multi: '',
});
// Forward inputs directly
expect(
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs,
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
});
// Forward inputs separately
expect(
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
opt: inputs.opt,
single: inputs.single,
multi: inputs.multi,
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
});
// Overriding based on original input
expect(
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
single: [
testDataRef1(`override-${inputs.single.get(testDataRef1)}`),
testDataRef2('new-singleOpt'),
],
multi: inputs.multi.map(i => [
testDataRef1(`override-${i.get(testDataRef1)}`),
]),
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'none',
single: 'override-orig-single',
singleOpt: 'new-singleOpt',
multi: 'override-orig-multi1,override-orig-multi2',
});
// Mismatched input override length
expect(() =>
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
...inputs,
multi: [[testDataRef1('multi1')]],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to instantiate extension 'subject', override data provided for input 'multi' must match the length of the original inputs"`,
);
// Required input not provided
expect(() =>
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
...inputs,
single: [testDataRef2('singleOpt')],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to instantiate extension 'subject', missing required extension data value(s) 'test1'"`,
);
// Wrong value provided
expect(() =>
createExtensionTester(
subject.override({
factory(originalFactory) {
return originalFactory({
inputs: {
// @ts-expect-error
opt: [testDataRef2('opt')],
// @ts-expect-error
single: [testDataRef1('single'), outputRef({})],
multi: [
// @ts-expect-error
[testDataRef2('multi1')],
],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to instantiate extension 'subject', extension data 'test2' was provided but not declared"`,
);
});
});
});
@@ -17,7 +17,11 @@
import { AppNode } from '../apis';
import { PortableSchema, createSchemaFromZod } from '../schema';
import { Expand } from '../types';
import { createDataContainer } from './createExtensionBlueprint';
import {
ResolveInputValueOverrides,
createDataContainer,
resolveInputOverrides,
} from './createExtensionBlueprint';
import {
AnyExtensionDataRef,
ExtensionDataRef,
@@ -259,7 +263,7 @@ export interface ExtensionDefinition<
factory(
originalFactory: (context?: {
config?: TConfig;
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
}) => ExtensionDataContainer<UOutput>,
context: {
node: AppNode;
@@ -558,7 +562,7 @@ export function createExtension<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
}) => ExtensionDataContainer<UOutput>,
context: {
node: AppNode;
@@ -646,14 +650,19 @@ export function createExtension<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
}): ExtensionDataContainer<UOutput> => {
return createDataContainer<UOutput>(
newOptions.factory({
node,
config: innerContext?.config ?? config,
inputs: (innerContext?.inputs ?? inputs) as any, // TODO: Fix the way input values are overridden
inputs: resolveInputOverrides(
newOptions.inputs,
inputs,
innerContext?.inputs,
) as any, // TODO: Might be able to improve this once legacy inputs are gone
}) as Iterable<any>,
newOptions.output,
);
},
{
@@ -16,9 +16,15 @@
import React from 'react';
import { coreExtensionData } from './coreExtensionData';
import { createExtensionBlueprint } from './createExtensionBlueprint';
import {
createDataContainer,
createExtensionBlueprint,
} from './createExtensionBlueprint';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import { createExtensionDataRef } from './createExtensionDataRef';
import {
ExtensionDataValue,
createExtensionDataRef,
} from './createExtensionDataRef';
import { createExtensionInput } from './createExtensionInput';
import { RouteRef } from '../routing';
import {
@@ -28,12 +34,15 @@ import {
function unused(..._any: any[]) {}
function factoryOutput(ext: ExtensionDefinition<any, any>) {
function factoryOutput(
ext: ExtensionDefinition<any, any>,
inputs: unknown = undefined,
) {
const int = toInternalExtensionDefinition(ext);
if (int.version !== 'v2') {
throw new Error('Expected v2 extension');
}
return Array.from(int.factory({} as any));
return Array.from(int.factory({ inputs } as any));
}
describe('createExtensionBlueprint', () => {
@@ -335,6 +344,257 @@ describe('createExtensionBlueprint', () => {
expect(true).toBe(true);
});
it('should be able to override inputs when calling original factory', () => {
const outputRef = createExtensionDataRef<unknown>().with({ id: 'output' });
const testDataRef1 = createExtensionDataRef<string>().with({ id: 'test1' });
const testDataRef2 = createExtensionDataRef<string>().with({ id: 'test2' });
const Blueprint = createExtensionBlueprint({
kind: 'test-extension',
attachTo: { id: 'test', input: 'default' },
inputs: {
opt: createExtensionInput([testDataRef1.optional()], {
singleton: true,
optional: true,
}),
single: createExtensionInput([testDataRef1, testDataRef2.optional()], {
singleton: true,
}),
multi: createExtensionInput([testDataRef1]),
},
output: [outputRef],
factory(_, { inputs }) {
return [
outputRef({
opt: inputs.opt?.get(testDataRef1) ?? 'none',
single: inputs.single.get(testDataRef1),
singleOpt: inputs.single.get(testDataRef2) ?? 'none',
multi: inputs.multi.map(i => i.get(testDataRef1)).join(','),
}),
];
},
});
const mockInput = (node: string, ...data: ExtensionDataValue<any, any>[]) =>
Object.assign(createDataContainer(data), {
node,
});
const mockParentInputs = {
opt: mockInput('node-opt', testDataRef1('orig-opt')),
single: mockInput('node-single', testDataRef1('orig-single')),
multi: [
mockInput('node-multi1', testDataRef1('orig-multi1')),
mockInput('node-multi2', testDataRef1('orig-multi2')),
],
};
// All values provided
expect(
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory) {
return origFactory(
{},
{
inputs: {
opt: [testDataRef1('opt')],
single: [testDataRef1('single'), testDataRef2('singleOpt')],
multi: [[testDataRef1('multi1')], [testDataRef1('multi2')]],
},
},
);
},
}),
mockParentInputs,
),
).toEqual([
outputRef({
opt: 'opt',
single: 'single',
singleOpt: 'singleOpt',
multi: 'multi1,multi2',
}),
]);
// Minimal values provided
expect(
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory) {
return origFactory(
{},
{
inputs: {
single: [testDataRef1('single')],
multi: [],
},
},
);
},
}),
mockParentInputs,
),
).toEqual([
outputRef({
opt: 'none',
single: 'single',
singleOpt: 'none',
multi: '',
}),
]);
// Mismatched input override length
expect(() =>
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory, { inputs }) {
return origFactory(
{},
{
inputs: {
...inputs,
multi: [[testDataRef1('multi1')]],
},
},
);
},
}),
mockParentInputs,
),
).toThrowErrorMatchingInlineSnapshot(
`"override data provided for input 'multi' must match the length of the original inputs"`,
);
// Required input not provided
expect(() =>
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory, { inputs }) {
return origFactory(
{},
{
inputs: {
...inputs,
single: [testDataRef2('singleOpt')],
},
},
);
},
}),
mockParentInputs,
),
).toThrowErrorMatchingInlineSnapshot(
`"missing required extension data value(s) 'test1'"`,
);
// Wrong value provided
expect(() =>
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory) {
return origFactory(
{},
{
inputs: {
// @ts-expect-error
opt: [testDataRef2('opt')],
// @ts-expect-error
single: [testDataRef1('single'), outputRef({})],
multi: [
// @ts-expect-error
[testDataRef2('multi1')],
],
},
},
);
},
}),
mockParentInputs,
),
).toThrowErrorMatchingInlineSnapshot(
`"extension data 'test2' was provided but not declared"`,
);
// Forwarding entire inputs object
expect(
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory, { inputs }) {
return origFactory({}, { inputs });
},
}),
mockParentInputs,
),
).toEqual([
outputRef({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
}),
]);
// Forwarding individual outputs
expect(
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory, { inputs }) {
return origFactory(
{},
{
inputs: {
opt: inputs.opt,
single: inputs.single,
multi: inputs.multi,
},
},
);
},
}),
mockParentInputs,
),
).toEqual([
outputRef({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
}),
]);
// Overriding based on original input
expect(
factoryOutput(
Blueprint.makeWithOverrides({
factory(origFactory, { inputs }) {
return origFactory(
{},
{
inputs: {
single: [
testDataRef1(`override-${inputs.single.get(testDataRef1)}`),
testDataRef2('new-singleOpt'),
],
multi: inputs.multi.map(i => [
testDataRef1(`override-${i.get(testDataRef1)}`),
]),
},
},
);
},
}),
mockParentInputs,
),
).toEqual([
outputRef({
opt: 'none',
single: 'override-orig-single',
singleOpt: 'new-singleOpt',
multi: 'override-orig-multi1,override-orig-multi2',
}),
]);
});
it('should allow merging of inputs', () => {
const blueprint = createExtensionBlueprint({
kind: 'test-extension',
@@ -429,6 +689,43 @@ describe('createExtensionBlueprint', () => {
expect(factoryOutput(ext)).toEqual([testDataRef2('foobar')]);
});
it('should reject invalid output from original factory', () => {
const testDataRef1 = createExtensionDataRef<string>().with({ id: 'test1' });
const testDataRef2 = createExtensionDataRef<string>().with({ id: 'test2' });
expect(() =>
factoryOutput(
// @ts-expect-error
createExtensionBlueprint({
kind: 'test-extension',
attachTo: { id: 'test', input: 'default' },
output: [testDataRef1],
factory() {
return [testDataRef2('foo')];
},
}).makeWithOverrides({ factory: orig => orig({}) }),
),
).toThrowErrorMatchingInlineSnapshot(
`"extension data 'test2' was provided but not declared"`,
);
expect(() =>
factoryOutput(
// @ts-expect-error
createExtensionBlueprint({
kind: 'test-extension',
attachTo: { id: 'test', input: 'default' },
output: [testDataRef1],
factory() {
return [];
},
}).makeWithOverrides({ factory: orig => orig({}) }),
),
).toThrowErrorMatchingInlineSnapshot(
`"missing required extension data value(s) 'test1'"`,
);
});
it('should allow returning of the parent data container', () => {
const testDataRef1 = createExtensionDataRef<string>().with({ id: 'test1' });
const testDataRef2 = createExtensionDataRef<string>().with({ id: 'test2' });
@@ -20,6 +20,7 @@ import {
CreateExtensionOptions,
ExtensionDataContainer,
ExtensionDefinition,
ResolvedExtensionInput,
ResolvedExtensionInputs,
VerifyExtensionFactoryOutput,
createExtension,
@@ -29,6 +30,7 @@ import { ExtensionInput } from './createExtensionInput';
import {
AnyExtensionDataRef,
ExtensionDataRef,
ExtensionDataRefToValue,
ExtensionDataValue,
} from './createExtensionDataRef';
@@ -75,6 +77,61 @@ export type CreateExtensionBlueprintOptions<
dataRefs?: TDataRefs;
} & VerifyExtensionFactoryOutput<UOutput, UFactoryOutput>;
/** @public */
export type ResolveInputValueOverrides<
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
} = {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
> = Expand<
{
[KName in keyof TInputs as TInputs[KName] extends ExtensionInput<
any,
{
optional: infer IOptional extends boolean;
singleton: boolean;
}
>
? IOptional extends true
? never
: KName
: never]: TInputs[KName] extends ExtensionInput<
infer IDataRefs,
{ optional: boolean; singleton: infer ISingleton extends boolean }
>
? ISingleton extends true
? Iterable<ExtensionDataRefToValue<IDataRefs>>
: Array<Iterable<ExtensionDataRefToValue<IDataRefs>>>
: never;
} & {
[KName in keyof TInputs as TInputs[KName] extends ExtensionInput<
any,
{
optional: infer IOptional extends boolean;
singleton: boolean;
}
>
? IOptional extends true
? KName
: never
: never]?: TInputs[KName] extends ExtensionInput<
infer IDataRefs,
{ optional: boolean; singleton: infer ISingleton extends boolean }
>
? ISingleton extends true
? Iterable<ExtensionDataRefToValue<IDataRefs>>
: Array<Iterable<ExtensionDataRefToValue<IDataRefs>>>
: never;
}
>;
/**
* @public
*/
@@ -156,7 +213,7 @@ export interface ExtensionBlueprint<
params: TParams,
context?: {
config?: TConfig;
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
},
) => ExtensionDataContainer<UOutput>,
context: {
@@ -202,13 +259,34 @@ export function createDataContainer<UData extends AnyExtensionDataRef>(
? ExtensionDataValue<IData, IId>
: never
>,
declaredRefs?: ExtensionDataRef<any, any, any>[],
): ExtensionDataContainer<UData> {
const container = new Map<string, ExtensionDataValue<any, any>>();
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;
@@ -219,6 +297,84 @@ export function createDataContainer<UData extends AnyExtensionDataRef>(
} as ExtensionDataContainer<UData>;
}
function expectArray<T>(value: T | T[]): T[] {
return value as T[];
}
function expectItem<T>(value: T | T[]): T {
return value as T;
}
/** @internal */
export function resolveInputOverrides(
declaredInputs?: {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
inputs?: {
[KName in string]?:
| ({ node: AppNode } & ExtensionDataContainer<any>)
| Array<{ node: AppNode } & ExtensionDataContainer<any>>;
},
inputOverrides?: ResolveInputValueOverrides,
) {
if (!declaredInputs || !inputs || !inputOverrides) {
return inputs;
}
const newInputs: typeof inputs = {};
for (const name in declaredInputs) {
if (!Object.hasOwn(declaredInputs, name)) {
continue;
}
const declaredInput = declaredInputs[name];
const providedData = inputOverrides[name];
if (declaredInput.config.singleton) {
const originalInput = expectItem(inputs[name]);
if (providedData) {
const providedContainer = createDataContainer(
providedData as Iterable<ExtensionDataValue<any, any>>,
declaredInput.extensionData,
);
if (!originalInput) {
throw new Error(
`attempted to override data of input '${name}' but it is not present in the original inputs`,
);
}
newInputs[name] = Object.assign(providedContainer, {
name: (originalInput as ResolvedExtensionInput<any>).node,
}) as any;
}
} else {
const originalInput = expectArray(inputs[name]);
if (!Array.isArray(providedData)) {
throw new Error(
`override data provided for input '${name}' must be an array`,
);
}
if (
originalInput.length !== providedData.length &&
providedData.length > 0
) {
throw new Error(
`override data provided for input '${name}' must match the length of the original inputs`,
);
}
newInputs[name] = providedData.map((data, i) => {
const providedContainer = createDataContainer(
data as Iterable<ExtensionDataValue<any, any>>,
declaredInput.extensionData,
);
return Object.assign(providedContainer, {
name: (originalInput[i] as ResolvedExtensionInput<any>).node,
}) as any;
});
}
}
return newInputs;
}
/**
* @internal
*/
@@ -288,7 +444,7 @@ class ExtensionBlueprintImpl<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides<TInputs>;
},
) => ExtensionDataContainer<UOutput>,
context: {
@@ -347,15 +503,20 @@ class ExtensionBlueprintImpl<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
inputs?: ResolveInputValueOverrides;
},
): ExtensionDataContainer<UOutput> => {
return createDataContainer<UOutput>(
this.options.factory(innerParams, {
node,
config: innerContext?.config ?? config,
inputs: (innerContext?.inputs ?? inputs) as any, // TODO: Fix the way input values are overridden
inputs: resolveInputOverrides(
this.options.inputs,
inputs,
innerContext?.inputs,
) as any, // TODO: Might be able to improve this once legacy inputs are gone
}),
this.options.output,
);
},
{
@@ -56,6 +56,7 @@ export {
} from './types';
export {
type CreateExtensionBlueprintOptions,
type ResolveInputValueOverrides,
type ExtensionBlueprint,
createExtensionBlueprint,
} from './createExtensionBlueprint';