backend-plugin-api: fix for plugins and modules depending on multion services
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-plugin-api': patch
|
||||
---
|
||||
|
||||
Fixed a type issue where plugin and modules depending on multiton services would not receive the correct type.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Internal type refactor.
|
||||
@@ -245,6 +245,7 @@ Monorepo
|
||||
monorepos
|
||||
msgraph
|
||||
msw
|
||||
multiton
|
||||
mutex
|
||||
mutexes
|
||||
mysql
|
||||
|
||||
@@ -342,6 +342,62 @@ describe('BackendInitializer', () => {
|
||||
await init.start();
|
||||
});
|
||||
|
||||
it('should allow plugins and modules depend on multiton services', async () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const multiServiceRef = createServiceRef<string>({
|
||||
id: 'a',
|
||||
multiton: true,
|
||||
});
|
||||
const init = new BackendInitializer(baseFactories);
|
||||
|
||||
init.add(
|
||||
createServiceFactory({
|
||||
service: multiServiceRef,
|
||||
deps: {},
|
||||
factory: () => 'x',
|
||||
}),
|
||||
);
|
||||
init.add(
|
||||
createServiceFactory({
|
||||
service: multiServiceRef,
|
||||
deps: {},
|
||||
factory: () => 'y',
|
||||
}),
|
||||
);
|
||||
|
||||
init.add(
|
||||
createBackendPlugin({
|
||||
pluginId: 'test',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: { multi: multiServiceRef },
|
||||
async init({ multi }) {
|
||||
expect(multi).toEqual(['x', 'y']);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
init.add(
|
||||
createBackendModule({
|
||||
pluginId: 'test',
|
||||
moduleId: 'test',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: { multi: multiServiceRef },
|
||||
async init({ multi }) {
|
||||
expect(multi).toEqual(['x', 'y']);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await init.start();
|
||||
});
|
||||
|
||||
it('should forward errors when plugins fail to start', async () => {
|
||||
const init = new BackendInitializer([]);
|
||||
init.add(
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
AuthService,
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
HttpRouterService,
|
||||
ServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { RequestHandler } from 'express';
|
||||
@@ -107,7 +108,10 @@ export function makeLegacyPlugin<
|
||||
return [key, transform(dep)];
|
||||
}
|
||||
if (key === 'tokenManager') {
|
||||
return [key, wrapTokenManager(dep as TokenManager, _auth)];
|
||||
return [
|
||||
key,
|
||||
wrapTokenManager(dep as TokenManager, _auth as AuthService),
|
||||
];
|
||||
}
|
||||
return [key, dep];
|
||||
}),
|
||||
@@ -115,7 +119,7 @@ export function makeLegacyPlugin<
|
||||
const router = await createRouter(
|
||||
pluginEnv as TransformedEnv<TEnv, TEnvTransforms>,
|
||||
);
|
||||
_router.use(router);
|
||||
(_router as HttpRouterService).use(router);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -82,14 +82,12 @@ export interface BackendModuleRegistrationPoints {
|
||||
): void;
|
||||
// (undocumented)
|
||||
registerInit<
|
||||
Deps extends {
|
||||
[name in string]: unknown;
|
||||
TDeps extends {
|
||||
[name in string]: ServiceRef<unknown> | ExtensionPoint<unknown>;
|
||||
},
|
||||
>(options: {
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]> | ExtensionPoint<Deps[name]>;
|
||||
};
|
||||
init(deps: Deps): Promise<void>;
|
||||
deps: TDeps;
|
||||
init(deps: DepsToInstances<TDeps>): Promise<void>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
@@ -105,14 +103,12 @@ export interface BackendPluginRegistrationPoints {
|
||||
): void;
|
||||
// (undocumented)
|
||||
registerInit<
|
||||
Deps extends {
|
||||
[name in string]: unknown;
|
||||
TDeps extends {
|
||||
[name in string]: ServiceRef<unknown>;
|
||||
},
|
||||
>(options: {
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]>;
|
||||
};
|
||||
init(deps: Deps): Promise<void>;
|
||||
deps: TDeps;
|
||||
init(deps: DepsToInstances<TDeps>): Promise<void>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createServiceRef } from '../services';
|
||||
import { createBackendModule } from './createBackendModule';
|
||||
import { createExtensionPoint } from './createExtensionPoint';
|
||||
import { InternalBackendRegistrations } from './types';
|
||||
|
||||
describe('createBackendModule', () => {
|
||||
@@ -54,4 +56,35 @@ describe('createBackendModule', () => {
|
||||
// @ts-expect-error
|
||||
expect(module({ a: 'a' })).toBeDefined();
|
||||
});
|
||||
|
||||
it('should be able to depend on all types of dependencies', () => {
|
||||
const extensionPoint = createExtensionPoint<string>({ id: 'point' });
|
||||
const singleServiceRef = createServiceRef<string>({ id: 'single' });
|
||||
const multiServiceRef = createServiceRef<string>({
|
||||
id: 'multi',
|
||||
multiton: true,
|
||||
});
|
||||
|
||||
const plugin = createBackendModule({
|
||||
pluginId: 'x',
|
||||
moduleId: 'y',
|
||||
register(r) {
|
||||
r.registerInit({
|
||||
deps: {
|
||||
point: extensionPoint,
|
||||
single: singleServiceRef,
|
||||
multi: multiServiceRef,
|
||||
},
|
||||
async init({ point, single, multi }) {
|
||||
const a: string = point;
|
||||
const b: string = single;
|
||||
const c: string[] = multi;
|
||||
expect([a, b, c]).toBe('unused');
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
expect(plugin.$$type).toEqual('@backstage/BackendFeature');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createServiceRef } from '../services';
|
||||
import { createBackendPlugin } from './createBackendPlugin';
|
||||
import { createExtensionPoint } from './createExtensionPoint';
|
||||
import { InternalBackendRegistrations } from './types';
|
||||
|
||||
describe('createBackendPlugin', () => {
|
||||
@@ -52,4 +54,50 @@ describe('createBackendPlugin', () => {
|
||||
// @ts-expect-error
|
||||
expect(plugin({ a: 'a' })).toBeDefined();
|
||||
});
|
||||
|
||||
it('should be able to depend on all compatible dependencies', () => {
|
||||
const singleServiceRef = createServiceRef<string>({ id: 'single' });
|
||||
const multiServiceRef = createServiceRef<string>({
|
||||
id: 'multi',
|
||||
multiton: true,
|
||||
});
|
||||
|
||||
const plugin = createBackendPlugin({
|
||||
pluginId: 'x',
|
||||
register(r) {
|
||||
r.registerInit({
|
||||
deps: {
|
||||
single: singleServiceRef,
|
||||
multi: multiServiceRef,
|
||||
},
|
||||
async init({ single, multi }) {
|
||||
const a: string = single;
|
||||
const b: string[] = multi;
|
||||
expect([a, b]).toBe('unused');
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
expect(plugin.$$type).toEqual('@backstage/BackendFeature');
|
||||
});
|
||||
|
||||
it('should not be able to depend on extension points', () => {
|
||||
const extensionPoint = createExtensionPoint<string>({ id: 'point' });
|
||||
|
||||
const plugin = createBackendPlugin({
|
||||
pluginId: 'x',
|
||||
register(r) {
|
||||
r.registerInit({
|
||||
deps: {
|
||||
// @ts-expect-error
|
||||
point: extensionPoint,
|
||||
},
|
||||
async init() {},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
expect(plugin.$$type).toEqual('@backstage/BackendFeature');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,17 @@ export type ExtensionPoint<T> = {
|
||||
$$type: '@backstage/ExtensionPoint';
|
||||
};
|
||||
|
||||
/** @ignore */
|
||||
type DepsToInstances<
|
||||
TDeps extends {
|
||||
[key in string]: ServiceRef<unknown> | ExtensionPoint<unknown>;
|
||||
},
|
||||
> = {
|
||||
[key in keyof TDeps]: TDeps[key] extends ServiceRef<unknown, any, 'multiton'>
|
||||
? Array<TDeps[key]['T']>
|
||||
: TDeps[key]['T'];
|
||||
};
|
||||
|
||||
/**
|
||||
* The callbacks passed to the `register` method of a backend plugin.
|
||||
*
|
||||
@@ -46,11 +57,13 @@ export interface BackendPluginRegistrationPoints {
|
||||
ref: ExtensionPoint<TExtensionPoint>,
|
||||
impl: TExtensionPoint,
|
||||
): void;
|
||||
registerInit<Deps extends { [name in string]: unknown }>(options: {
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]>;
|
||||
};
|
||||
init(deps: Deps): Promise<void>;
|
||||
registerInit<
|
||||
TDeps extends {
|
||||
[name in string]: ServiceRef<unknown>;
|
||||
},
|
||||
>(options: {
|
||||
deps: TDeps;
|
||||
init(deps: DepsToInstances<TDeps>): Promise<void>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
@@ -64,11 +77,13 @@ export interface BackendModuleRegistrationPoints {
|
||||
ref: ExtensionPoint<TExtensionPoint>,
|
||||
impl: TExtensionPoint,
|
||||
): void;
|
||||
registerInit<Deps extends { [name in string]: unknown }>(options: {
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]> | ExtensionPoint<Deps[name]>;
|
||||
};
|
||||
init(deps: Deps): Promise<void>;
|
||||
registerInit<
|
||||
TDeps extends {
|
||||
[name in string]: ServiceRef<unknown> | ExtensionPoint<unknown>;
|
||||
},
|
||||
>(options: {
|
||||
deps: TDeps;
|
||||
init(deps: DepsToInstances<TDeps>): Promise<void>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user