backend-app-api: validate extension point deps
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Add validation to make sure that extension points do not cross plugin boundaries.
|
||||
@@ -294,4 +294,36 @@ describe('BackendInitializer', () => {
|
||||
"Circular dependency detected for modules of plugin 'test', 'modA' -> 'modB' -> 'modA'",
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject modules that depend on extension points other plugins', async () => {
|
||||
const init = new BackendInitializer(new ServiceRegistry(baseFactories));
|
||||
const extA = createExtensionPoint<string>({ id: 'a' });
|
||||
init.add(
|
||||
createBackendPlugin({
|
||||
pluginId: 'testA',
|
||||
register(reg) {
|
||||
reg.registerExtensionPoint(extA, 'a');
|
||||
reg.registerInit({
|
||||
deps: {},
|
||||
async init() {},
|
||||
});
|
||||
},
|
||||
})(),
|
||||
);
|
||||
init.add(
|
||||
createBackendModule({
|
||||
pluginId: 'testB',
|
||||
moduleId: 'mod',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: { ext: extA },
|
||||
async init() {},
|
||||
});
|
||||
},
|
||||
})(),
|
||||
);
|
||||
await expect(init.start()).rejects.toThrow(
|
||||
"Extension point registered for plugin 'testA' may not be used by module for plugin 'testB'",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,10 @@ export interface BackendRegisterInit {
|
||||
export class BackendInitializer {
|
||||
#startPromise?: Promise<void>;
|
||||
#features = new Array<InternalBackendFeature>();
|
||||
#extensionPoints = new Map<ExtensionPoint<unknown>, unknown>();
|
||||
#extensionPoints = new Map<
|
||||
ExtensionPoint<unknown>,
|
||||
{ impl: unknown; pluginId: string }
|
||||
>();
|
||||
#serviceHolder: EnumerableServiceHolder;
|
||||
|
||||
constructor(serviceHolder: EnumerableServiceHolder) {
|
||||
@@ -57,11 +60,14 @@ export class BackendInitializer {
|
||||
const missingRefs = new Set<ServiceOrExtensionPoint>();
|
||||
|
||||
for (const [name, ref] of Object.entries(deps)) {
|
||||
const extensionPoint = this.#extensionPoints.get(
|
||||
ref as ExtensionPoint<unknown>,
|
||||
);
|
||||
if (extensionPoint) {
|
||||
result.set(name, extensionPoint);
|
||||
const ep = this.#extensionPoints.get(ref as ExtensionPoint<unknown>);
|
||||
if (ep) {
|
||||
if (ep.pluginId !== pluginId) {
|
||||
throw new Error(
|
||||
`Extension point registered for plugin '${ep.pluginId}' may not be used by module for plugin '${pluginId}'`,
|
||||
);
|
||||
}
|
||||
result.set(name, ep.impl);
|
||||
} else {
|
||||
const impl = await this.#serviceHolder.get(
|
||||
ref as ServiceRef<unknown>,
|
||||
@@ -169,7 +175,10 @@ export class BackendInitializer {
|
||||
`ExtensionPoint with ID '${extRef.id}' is already registered`,
|
||||
);
|
||||
}
|
||||
this.#extensionPoints.set(extRef, extImpl);
|
||||
this.#extensionPoints.set(extRef, {
|
||||
impl: extImpl,
|
||||
pluginId: r.pluginId,
|
||||
});
|
||||
provides.add(extRef);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user