frontend-app-api: switch undeclared inputs to be a warning instead of error
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': minor
|
||||
---
|
||||
|
||||
Attaching extensions to an input that does not exist is now a warning rather than an error.
|
||||
@@ -30,6 +30,7 @@ import { AppNodeSpec } from '@backstage/frontend-plugin-api';
|
||||
import { resolveAppTree } from './resolveAppTree';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';
|
||||
import { withLogCollector } from '@backstage/test-utils';
|
||||
|
||||
const testDataRef = createExtensionDataRef<string>('test');
|
||||
const otherDataRef = createExtensionDataRef<number>('other');
|
||||
@@ -433,8 +434,8 @@ describe('createAppNodeInstance', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should refuse to create an instance with undeclared inputs', () => {
|
||||
expect(() =>
|
||||
it('should warn when creating an instance with undeclared inputs', () => {
|
||||
const { warn } = withLogCollector(['warn'], () =>
|
||||
createAppNodeInstance({
|
||||
attachments: new Map([
|
||||
[
|
||||
@@ -458,7 +459,7 @@ describe('createAppNodeInstance', () => {
|
||||
resolveExtensionDefinition(
|
||||
createExtension({
|
||||
namespace: 'app',
|
||||
name: 'test',
|
||||
name: 'parent',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
inputs: {
|
||||
declared: createExtensionInput({
|
||||
@@ -471,13 +472,15 @@ describe('createAppNodeInstance', () => {
|
||||
),
|
||||
),
|
||||
}),
|
||||
).toThrow(
|
||||
"Failed to instantiate extension 'app/test', received undeclared input 'undeclared' from extension 'app/test'",
|
||||
);
|
||||
|
||||
expect(warn).toEqual([
|
||||
"The extension 'app/test' is attached to the input 'undeclared' of 'app/parent', but the extension 'app/parent' noes not declare a 'undeclared' input",
|
||||
]);
|
||||
});
|
||||
|
||||
it('should refuse to create an instance with multiple undeclared inputs', () => {
|
||||
expect(() =>
|
||||
const { warn } = withLogCollector(['warn'], () =>
|
||||
createAppNodeInstance({
|
||||
attachments: new Map([
|
||||
[
|
||||
@@ -496,7 +499,7 @@ describe('createAppNodeInstance', () => {
|
||||
resolveExtensionDefinition(
|
||||
createExtension({
|
||||
namespace: 'app',
|
||||
name: 'test',
|
||||
name: 'parent',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
output: {},
|
||||
factory: () => ({}),
|
||||
@@ -504,9 +507,12 @@ describe('createAppNodeInstance', () => {
|
||||
),
|
||||
),
|
||||
}),
|
||||
).toThrow(
|
||||
"Failed to instantiate extension 'app/test', received undeclared inputs 'undeclared1' from extension 'app/test' and 'undeclared2' from extensions 'app/test', 'app/test'",
|
||||
);
|
||||
|
||||
expect(warn).toEqual([
|
||||
"The extension 'app/test' is attached to the input 'undeclared1' of 'app/parent', but the extension 'app/parent' noes not declare a 'undeclared1' input",
|
||||
"The extensions 'app/test', 'app/test' are attached to the input 'undeclared2' of 'app/parent', but the extension 'app/parent' noes not declare a 'undeclared2' input",
|
||||
]);
|
||||
});
|
||||
|
||||
it('should refuse to create an instance with multiple inputs for required singleton', () => {
|
||||
|
||||
@@ -46,26 +46,26 @@ function resolveInputData(
|
||||
}
|
||||
|
||||
function resolveInputs(
|
||||
id: string,
|
||||
inputMap: AnyExtensionInputMap,
|
||||
attachments: ReadonlyMap<string, AppNode[]>,
|
||||
): ResolvedExtensionInputs<AnyExtensionInputMap> {
|
||||
const undeclaredAttachments = Array.from(attachments.entries()).filter(
|
||||
([inputName]) => inputMap[inputName] === undefined,
|
||||
);
|
||||
// TODO: Make this a warning rather than an error
|
||||
if (undeclaredAttachments.length > 0) {
|
||||
throw new Error(
|
||||
`received undeclared input${
|
||||
undeclaredAttachments.length > 1 ? 's' : ''
|
||||
} ${undeclaredAttachments
|
||||
.map(
|
||||
([k, exts]) =>
|
||||
`'${k}' from extension${exts.length > 1 ? 's' : ''} '${exts
|
||||
.map(e => e.spec.id)
|
||||
.join("', '")}'`,
|
||||
)
|
||||
.join(' and ')}`,
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
for (const [name, nodes] of undeclaredAttachments) {
|
||||
const pl = nodes.length > 1;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`The extension${pl ? 's' : ''} '${nodes
|
||||
.map(n => n.spec.id)
|
||||
.join("', '")}' ${
|
||||
pl ? 'are' : 'is'
|
||||
} attached to the input '${name}' of '${id}', but the extension '${id}' noes not declare a '${name}' input`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return mapValues(inputMap, (input, inputName) => {
|
||||
@@ -129,7 +129,7 @@ export function createAppNodeInstance(options: {
|
||||
const namedOutputs = internalExtension.factory({
|
||||
node,
|
||||
config: parsedConfig,
|
||||
inputs: resolveInputs(internalExtension.inputs, attachments),
|
||||
inputs: resolveInputs(id, internalExtension.inputs, attachments),
|
||||
});
|
||||
|
||||
for (const [name, output] of Object.entries(namedOutputs)) {
|
||||
|
||||
Reference in New Issue
Block a user