frontend-app-api: filter out failed attachments
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -6,3 +6,4 @@ Updated error reporting and app tree resolution logic to attribute errors to the
|
||||
|
||||
- If an attachment fails to provide the required input data, the error is now attributed to the attachment rather than the parent extension.
|
||||
- Singleton extension inputs will now only forward attachment errors if the input is required.
|
||||
- Array extension inputs will now filter out failed attachments instead of failing the entire app tree resolution.
|
||||
|
||||
@@ -1677,6 +1677,55 @@ describe('instantiateAppNodeTree', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter out failed attachments for non-singleton inputs', () => {
|
||||
const node = makeNode(
|
||||
resolveExtensionDefinition(
|
||||
createExtension({
|
||||
name: 'test',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
inputs: {
|
||||
children: createExtensionInput([otherDataRef]),
|
||||
},
|
||||
output: [inputCountRef],
|
||||
factory: ({ inputs }) => [
|
||||
inputCountRef(inputs.children.length),
|
||||
],
|
||||
}),
|
||||
{ namespace: 'app' },
|
||||
),
|
||||
);
|
||||
expect(
|
||||
createAppNodeInstance({
|
||||
apis: testApis,
|
||||
attachments: new Map([
|
||||
[
|
||||
'children',
|
||||
[
|
||||
attachmentWithoutRequiredData,
|
||||
makeInstanceWithId(simpleExtension, {
|
||||
other: 42,
|
||||
}),
|
||||
],
|
||||
],
|
||||
]),
|
||||
node,
|
||||
collector,
|
||||
})?.getData(inputCountRef),
|
||||
).toBe(1);
|
||||
|
||||
expect(collector.collectErrors()).toEqual([
|
||||
{
|
||||
code: 'EXTENSION_INPUT_DATA_MISSING',
|
||||
message:
|
||||
"extension 'app/test' could not be attached because its output data ('test') does not match what the input 'children' requires ('other')",
|
||||
context: {
|
||||
node: attachmentWithoutRequiredData,
|
||||
inputName: 'children',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,21 +38,22 @@ const INSTANTIATION_FAILED = new Error('Instantiation failed');
|
||||
function mapWithFailures<T, U>(
|
||||
iterable: Iterable<T>,
|
||||
callback: (item: T) => U,
|
||||
options?: { ignoreFailures?: boolean },
|
||||
): U[] {
|
||||
let failed = false;
|
||||
const results = Array.from(iterable).map(item => {
|
||||
const results = [];
|
||||
for (const item of iterable) {
|
||||
try {
|
||||
return callback(item);
|
||||
results.push(callback(item));
|
||||
} catch (error) {
|
||||
if (error === INSTANTIATION_FAILED) {
|
||||
failed = true;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
return null as any;
|
||||
}
|
||||
});
|
||||
if (failed) {
|
||||
}
|
||||
if (failed && !options?.ignoreFailures) {
|
||||
throw INSTANTIATION_FAILED;
|
||||
}
|
||||
return results;
|
||||
@@ -292,13 +293,16 @@ function resolveV2Inputs(
|
||||
}
|
||||
}
|
||||
|
||||
return mapWithFailures(attachedNodes, attachment =>
|
||||
resolveInputDataContainer(
|
||||
input.extensionData,
|
||||
attachment,
|
||||
inputName,
|
||||
collector,
|
||||
),
|
||||
return mapWithFailures(
|
||||
attachedNodes,
|
||||
attachment =>
|
||||
resolveInputDataContainer(
|
||||
input.extensionData,
|
||||
attachment,
|
||||
inputName,
|
||||
collector,
|
||||
),
|
||||
{ ignoreFailures: true },
|
||||
);
|
||||
}) as ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user