backend-app-api: more informative error messages for plugin and module startup failures

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-05-25 14:38:25 +02:00
parent 0a213ab691
commit 0f02bdcbd7
2 changed files with 57 additions and 3 deletions
@@ -18,6 +18,8 @@ import {
createServiceRef,
createServiceFactory,
coreServices,
createBackendPlugin,
createBackendModule,
} from '@backstage/backend-plugin-api';
import { BackendInitializer } from './BackendInitializer';
import { ServiceRegistry } from './ServiceRegistry';
@@ -72,4 +74,45 @@ describe('BackendInitializer', () => {
expect(rootFactory).toHaveBeenCalled();
expect(pluginFactory).not.toHaveBeenCalled();
});
it('should forward errors when plugins fail to start', async () => {
const init = new BackendInitializer(new ServiceRegistry([]));
init.add(
createBackendPlugin({
pluginId: 'test',
register(reg) {
reg.registerInit({
deps: {},
async init() {
throw new Error('NOPE');
},
});
},
})(),
);
await expect(init.start()).rejects.toThrow(
"Plugin 'test' startup failed; caused by Error: NOPE",
);
});
it('should forward errors when modules fail to start', async () => {
const init = new BackendInitializer(new ServiceRegistry([]));
init.add(
createBackendModule({
pluginId: 'test',
moduleId: 'mod',
register(reg) {
reg.registerInit({
deps: {},
async init() {
throw new Error('NOPE');
},
});
},
})(),
);
await expect(init.start()).rejects.toThrow(
"Module 'mod' for plugin 'test' startup failed; caused by Error: NOPE",
);
});
});
@@ -26,6 +26,7 @@ import { EnumerableServiceHolder, ServiceOrExtensionPoint } from './types';
// Direct internal import to avoid duplication
// eslint-disable-next-line @backstage/no-forbidden-package-imports
import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring/types';
import { ForwardedError } from '@backstage/errors';
export interface BackendRegisterInit {
consumes: Set<ServiceOrExtensionPoint>;
@@ -194,12 +195,17 @@ export class BackendInitializer {
// Modules are initialized before plugins, so that they can provide extension to the plugin
const modules = moduleInits.get(pluginId) ?? [];
await Promise.all(
Array.from(modules.values()).map(async moduleInit => {
Array.from(modules).map(async ([moduleId, moduleInit]) => {
const moduleDeps = await this.#getInitDeps(
moduleInit.init.deps,
pluginId,
);
await moduleInit.init.func(moduleDeps);
await moduleInit.init.func(moduleDeps).catch(error => {
throw new ForwardedError(
`Module '${moduleId}' for plugin '${pluginId}' startup failed`,
error,
);
});
}),
);
@@ -211,7 +217,12 @@ export class BackendInitializer {
pluginInit.init.deps,
pluginId,
);
await pluginInit.init.func(pluginDeps);
await pluginInit.init.func(pluginDeps).catch(error => {
throw new ForwardedError(
`Plugin '${pluginId}' startup failed`,
error,
);
});
}
// Once the plugin and all modules have been initialized, we can signal that the plugin has stared up successfully