Merge pull request #25593 from backstage/mob/nbs10-service-modules

[NBS 1.0] Enabling multiple service implementations
This commit is contained in:
Patrik Oldsberg
2024-08-06 15:09:23 +02:00
committed by GitHub
40 changed files with 730 additions and 251 deletions
+133 -33
View File
@@ -156,7 +156,12 @@ export namespace mockServices {
// (undocumented)
export namespace auth {
const // (undocumented)
factory: ServiceFactoryCompat<AuthService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
AuthService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<AuthService> | undefined,
@@ -165,7 +170,12 @@ export namespace mockServices {
// (undocumented)
export namespace cache {
const // (undocumented)
factory: ServiceFactoryCompat<CacheService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
CacheService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<CacheService> | undefined,
@@ -174,7 +184,12 @@ export namespace mockServices {
// (undocumented)
export namespace database {
const // (undocumented)
factory: ServiceFactoryCompat<DatabaseService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
DatabaseService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<DatabaseService> | undefined,
@@ -185,7 +200,12 @@ export namespace mockServices {
// (undocumented)
export namespace discovery {
const // (undocumented)
factory: ServiceFactoryCompat<DiscoveryService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
DiscoveryService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<DiscoveryService> | undefined,
@@ -194,7 +214,12 @@ export namespace mockServices {
// (undocumented)
export namespace events {
const // (undocumented)
factory: ServiceFactoryCompat<EventsService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
EventsService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<EventsService> | undefined,
@@ -208,8 +233,8 @@ export namespace mockServices {
export namespace httpAuth {
const factory: ((options?: {
defaultCredentials?: BackstageCredentials;
}) => ServiceFactory<HttpAuthService, 'plugin'>) &
ServiceFactory<HttpAuthService, 'plugin'>;
}) => ServiceFactory<HttpAuthService, 'plugin', 'singleton'>) &
ServiceFactory<HttpAuthService, 'plugin', 'singleton'>;
const // (undocumented)
mock: (
partialImpl?: Partial<HttpAuthService> | undefined,
@@ -218,7 +243,12 @@ export namespace mockServices {
// (undocumented)
export namespace httpRouter {
const // (undocumented)
factory: ServiceFactoryCompat<HttpRouterService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
HttpRouterService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<HttpRouterService> | undefined,
@@ -229,7 +259,12 @@ export namespace mockServices {
// (undocumented)
export namespace identity {
const // (undocumented)
factory: ServiceFactoryCompat<IdentityService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
IdentityService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<IdentityService> | undefined,
@@ -238,7 +273,12 @@ export namespace mockServices {
// (undocumented)
export namespace lifecycle {
const // (undocumented)
factory: ServiceFactoryCompat<LifecycleService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
LifecycleService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<LifecycleService> | undefined,
@@ -247,7 +287,12 @@ export namespace mockServices {
// (undocumented)
export namespace logger {
const // (undocumented)
factory: ServiceFactoryCompat<LoggerService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
LoggerService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<LoggerService> | undefined,
@@ -256,7 +301,12 @@ export namespace mockServices {
// (undocumented)
export namespace permissions {
const // (undocumented)
factory: ServiceFactoryCompat<PermissionsService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
PermissionsService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<PermissionsService> | undefined,
@@ -271,15 +321,28 @@ export namespace mockServices {
data?: JsonObject;
};
const // (undocumented)
factory: ServiceFactory<RootConfigService, 'root'> &
factory: ServiceFactory<
RootConfigService,
'root',
'singleton' | 'multiton'
> &
((
options?: Options | undefined,
) => ServiceFactory<RootConfigService, 'root'>);
) => ServiceFactory<
RootConfigService,
'root',
'singleton' | 'multiton'
>);
}
// (undocumented)
export namespace rootHealth {
const // (undocumented)
factory: ServiceFactoryCompat<RootHealthService, 'root', undefined>;
factory: ServiceFactoryCompat<
RootHealthService,
'root',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<RootHealthService> | undefined,
@@ -290,8 +353,8 @@ export namespace mockServices {
const // (undocumented)
factory: ((
options?: RootHttpRouterFactoryOptions | undefined,
) => ServiceFactory<RootHttpRouterService, 'root'>) &
ServiceFactory<RootHttpRouterService, 'root'>;
) => ServiceFactory<RootHttpRouterService, 'root', 'singleton'>) &
ServiceFactory<RootHttpRouterService, 'root', 'singleton'>;
const // (undocumented)
mock: (
partialImpl?: Partial<RootHttpRouterService> | undefined,
@@ -300,7 +363,12 @@ export namespace mockServices {
// (undocumented)
export namespace rootLifecycle {
const // (undocumented)
factory: ServiceFactoryCompat<RootLifecycleService, 'root', undefined>;
factory: ServiceFactoryCompat<
RootLifecycleService,
'root',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<RootLifecycleService> | undefined,
@@ -315,10 +383,10 @@ export namespace mockServices {
level?: 'none' | 'error' | 'warn' | 'info' | 'debug';
};
const // (undocumented)
factory: ServiceFactory<LoggerService, 'root'> &
factory: ServiceFactory<LoggerService, 'root', 'singleton' | 'multiton'> &
((
options?: Options | undefined,
) => ServiceFactory<LoggerService, 'root'>);
) => ServiceFactory<LoggerService, 'root', 'singleton' | 'multiton'>);
const // (undocumented)
mock: (
partialImpl?: Partial<RootLoggerService> | undefined,
@@ -327,7 +395,12 @@ export namespace mockServices {
// (undocumented)
export namespace scheduler {
const // (undocumented)
factory: ServiceFactoryCompat<SchedulerService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
SchedulerService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<SchedulerService> | undefined,
@@ -338,7 +411,12 @@ export namespace mockServices {
// (undocumented)
export namespace tokenManager {
const // (undocumented)
factory: ServiceFactoryCompat<TokenManagerService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
TokenManagerService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<TokenManagerService> | undefined,
@@ -347,7 +425,12 @@ export namespace mockServices {
// (undocumented)
export namespace urlReader {
const // (undocumented)
factory: ServiceFactoryCompat<UrlReaderService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
UrlReaderService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<UrlReaderService> | undefined,
@@ -358,7 +441,12 @@ export namespace mockServices {
): UserInfoService;
// (undocumented)
export namespace userInfo {
const factory: ServiceFactoryCompat<UserInfoService, 'plugin', undefined>;
const factory: ServiceFactoryCompat<
UserInfoService,
'plugin',
'singleton',
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<UserInfoService> | undefined,
@@ -374,22 +462,34 @@ export function registerMswTestHooks(worker: {
}): void;
// @public
export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
static from<TService, TScope extends 'root' | 'plugin'>(
subject: ServiceFactory<TService, TScope>,
export class ServiceFactoryTester<
TService,
TScope extends 'root' | 'plugin',
TInstances extends 'singleton' | 'multiton' = 'singleton',
> {
static from<
TService,
TScope extends 'root' | 'plugin',
TInstances extends 'singleton' | 'multiton' = 'singleton',
>(
subject: ServiceFactory<TService, TScope, TInstances>,
options?: ServiceFactoryTesterOptions,
): ServiceFactoryTester<TService, TScope>;
): ServiceFactoryTester<TService, TScope, TInstances>;
// @deprecated
get(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService>;
getService<TGetService, TGetScope extends 'root' | 'plugin'>(
service: ServiceRef<TGetService, TGetScope>,
): Promise<TInstances extends 'multiton' ? TService[] : TService>;
getService<
TGetService,
TGetScope extends 'root' | 'plugin',
TGetInstances extends 'singleton' | 'multiton' = 'singleton',
>(
service: ServiceRef<TGetService, TGetScope, TGetInstances>,
...args: 'root' extends TGetScope ? [] : [pluginId?: string]
): Promise<TGetService>;
): Promise<TGetInstances extends 'multiton' ? TGetService[] : TGetService>;
getSubject(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService>;
): Promise<TInstances extends 'multiton' ? TService[] : TService>;
}
// @public
@@ -43,8 +43,12 @@ export interface ServiceFactoryTesterOptions {
*
* @public
*/
export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
readonly #subject: ServiceRef<TService, TScope>;
export class ServiceFactoryTester<
TService,
TScope extends 'root' | 'plugin',
TInstances extends 'singleton' | 'multiton' = 'singleton',
> {
readonly #subject: ServiceRef<TService, TScope, TInstances>;
readonly #registry: ServiceRegistry;
/**
@@ -54,10 +58,14 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
* @param options - Additional options
* @returns A new tester instance for the provided subject.
*/
static from<TService, TScope extends 'root' | 'plugin'>(
subject: ServiceFactory<TService, TScope>,
static from<
TService,
TScope extends 'root' | 'plugin',
TInstances extends 'singleton' | 'multiton' = 'singleton',
>(
subject: ServiceFactory<TService, TScope, TInstances>,
options?: ServiceFactoryTesterOptions,
) {
): ServiceFactoryTester<TService, TScope, TInstances> {
const registry = ServiceRegistry.create([
...defaultServiceFactories,
...(options?.dependencies ?? []),
@@ -67,7 +75,7 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
}
private constructor(
subject: ServiceRef<TService, TScope>,
subject: ServiceRef<TService, TScope, TInstances>,
registry: ServiceRegistry,
) {
this.#subject = subject;
@@ -81,7 +89,7 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
*/
async get(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService> {
): Promise<TInstances extends 'multiton' ? TService[] : TService> {
return this.getSubject(...args);
}
@@ -97,9 +105,10 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
*/
async getSubject(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService> {
): Promise<TInstances extends 'multiton' ? TService[] : TService> {
const [pluginId] = args;
return this.#registry.get(this.#subject, pluginId ?? 'test')!;
const instance = this.#registry.get(this.#subject, pluginId ?? 'test')!;
return instance;
}
/**
@@ -109,10 +118,14 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
*
* A plugin ID can optionally be provided for plugin scoped services, otherwise the plugin ID 'test' is used.
*/
async getService<TGetService, TGetScope extends 'root' | 'plugin'>(
service: ServiceRef<TGetService, TGetScope>,
async getService<
TGetService,
TGetScope extends 'root' | 'plugin',
TGetInstances extends 'singleton' | 'multiton' = 'singleton',
>(
service: ServiceRef<TGetService, TGetScope, TGetInstances>,
...args: 'root' extends TGetScope ? [] : [pluginId?: string]
): Promise<TGetService> {
): Promise<TGetInstances extends 'multiton' ? TGetService[] : TGetService> {
const [pluginId] = args;
const instance = await this.#registry.get(service, pluginId ?? 'test');
if (instance === undefined) {