Merge pull request #25982 from backstage/rugvip/input-tweak

frontend-plugin-api: improved forwarding of inputs
This commit is contained in:
Patrik Oldsberg
2024-08-13 09:08:19 +02:00
committed by GitHub
2 changed files with 123 additions and 27 deletions
@@ -767,35 +767,37 @@ describe('createExtension', () => {
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(','),
multi: inputs.multi
.map(i => `${i.node.spec.id}=${i.get(testDataRef1)}`)
.join(','),
}),
];
},
});
const optExt = createExtension({
name: 'opt',
name: 'o',
attachTo: { id: 'subject', input: 'opt' },
output: [testDataRef1],
factory: () => [testDataRef1('orig-opt')],
});
const singleExt = createExtension({
name: 'single',
name: 's',
attachTo: { id: 'subject', input: 'single' },
output: [testDataRef1, testDataRef2.optional()],
factory: () => [testDataRef1('orig-single')],
});
const multi1Ext = createExtension({
name: 'multi1',
name: 'm1',
attachTo: { id: 'subject', input: 'multi' },
output: [testDataRef1],
factory: () => [testDataRef1('orig-multi1')],
});
const multi2Ext = createExtension({
name: 'multi2',
name: 'm2',
attachTo: { id: 'subject', input: 'multi' },
output: [testDataRef1],
factory: () => [testDataRef1('orig-multi2')],
@@ -812,7 +814,7 @@ describe('createExtension', () => {
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
multi: 'm1=orig-multi1,m2=orig-multi2',
});
// All values provided
@@ -839,7 +841,7 @@ describe('createExtension', () => {
opt: 'opt',
single: 'single',
singleOpt: 'singleOpt',
multi: 'multi1,multi2',
multi: 'm1=multi1,m2=multi2',
});
// Minimal values provided
@@ -888,7 +890,7 @@ describe('createExtension', () => {
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
multi: 'm1=orig-multi1,m2=orig-multi2',
});
// Forward inputs separately
@@ -915,7 +917,61 @@ describe('createExtension', () => {
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'orig-multi1,orig-multi2',
multi: 'm1=orig-multi1,m2=orig-multi2',
});
// Reordering inputs
expect(
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
opt: inputs.opt,
single: inputs.single,
multi: [inputs.multi[1], inputs.multi[0]],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'm2=orig-multi2,m1=orig-multi1',
});
// Filter out inputs
expect(
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
opt: inputs.opt,
single: inputs.single,
multi: inputs.multi.filter(i => i.node.spec.id.endsWith('2')),
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toEqual({
opt: 'orig-opt',
single: 'orig-single',
singleOpt: 'none',
multi: 'm2=orig-multi2',
});
// Overriding based on original input
@@ -946,7 +1002,7 @@ describe('createExtension', () => {
opt: 'none',
single: 'override-orig-single',
singleOpt: 'new-singleOpt',
multi: 'override-orig-multi1,override-orig-multi2',
multi: 'm1=override-orig-multi1,m2=override-orig-multi2',
});
// Mismatched input override length
@@ -972,6 +1028,29 @@ describe('createExtension', () => {
`"Failed to instantiate extension 'subject', override data provided for input 'multi' must match the length of the original inputs"`,
);
// Mix forward and data override
expect(() =>
createExtensionTester(
subject.override({
factory(originalFactory, { inputs }) {
return originalFactory({
inputs: {
...inputs,
multi: [inputs.multi[0], [testDataRef1('multi2')]],
},
});
},
}),
)
.add(optExt)
.add(singleExt)
.add(multi1Ext)
.add(multi2Ext)
.data(outputRef),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to instantiate extension 'subject', override data for input 'multi' may not mix forwarded inputs with data overrides"`,
);
// Required input not provided
expect(() =>
createExtensionTester(
@@ -129,7 +129,7 @@ export function resolveInputOverrides(
);
}
newInputs[name] = Object.assign(providedContainer, {
name: (originalInput as ResolvedExtensionInput<any>).node,
node: (originalInput as ResolvedExtensionInput<any>).node,
}) as any;
}
} else {
@@ -139,23 +139,40 @@ export function resolveInputOverrides(
`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`,
);
// Regular inputs can be overridden in two different ways:
// 1) Forward a subset of the original inputs in a new order
// 2) Provide new data for each original input
// First check if all inputs are being removed
if (providedData.length === 0) {
newInputs[name] = [];
} else {
// Check how many of the provided data items have a node property, i.e. is a forwarded input
const withNodesCount = providedData.filter(d => 'node' in d).length;
if (withNodesCount === 0) {
if (originalInput.length !== providedData.length) {
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 = createExtensionDataContainer(
data as Iterable<ExtensionDataValue<any, any>>,
declaredInput.extensionData,
);
return Object.assign(providedContainer, {
node: (originalInput[i] as ResolvedExtensionInput<any>).node,
}) as any;
});
} else if (withNodesCount === providedData.length) {
newInputs[name] = providedData as any;
} else {
throw new Error(
`override data for input '${name}' may not mix forwarded inputs with data overrides`,
);
}
}
newInputs[name] = providedData.map((data, i) => {
const providedContainer = createExtensionDataContainer(
data as Iterable<ExtensionDataValue<any, any>>,
declaredInput.extensionData,
);
return Object.assign(providedContainer, {
name: (originalInput[i] as ResolvedExtensionInput<any>).node,
}) as any;
});
}
}
return newInputs;