frontend-plugin-api: add support for overriding input values when overriding extensions
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -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: [[testDataRef1('multi1')], [testDataRef1('multi2')]],
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
)
|
||||
.add(optExt)
|
||||
.add(singleExt)
|
||||
.add(multi1Ext)
|
||||
.add(multi2Ext)
|
||||
.data(outputRef),
|
||||
).toEqual({
|
||||
opt: 'none',
|
||||
single: 'single',
|
||||
singleOpt: 'none',
|
||||
multi: 'multi1,multi2',
|
||||
});
|
||||
|
||||
// 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', Invalid override provided for input 'multi', when overriding the input data the length must match the original input data"`,
|
||||
);
|
||||
|
||||
// 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 data values for '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', Invalid data value provided, 'test2' was 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,
|
||||
);
|
||||
},
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ export type CreateExtensionBlueprintOptions<
|
||||
} & VerifyExtensionFactoryOutput<UOutput, UFactoryOutput>;
|
||||
|
||||
/** @ignore */
|
||||
type ResolveInputValueOverrides<
|
||||
export type ResolveInputValueOverrides<
|
||||
TInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
|
||||
Reference in New Issue
Block a user