backend-plugin-api: make ServiceFactory opaque

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-02-08 11:13:58 +01:00
parent 7a05ac1b0c
commit b86efa2d04
10 changed files with 212 additions and 150 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/backend-test-utils': patch
'@backstage/backend-app-api': patch
---
Updated usage of `ServiceFactory`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-plugin-api': minor
---
Switch `ServiceFactory` to be an opaque type, keeping only the `service` field as public API, but also adding a type parameter for the service scope.
+45 -15
View File
@@ -48,7 +48,10 @@ export interface Backend {
}
// @public (undocumented)
export const cacheServiceFactory: () => ServiceFactory<PluginCacheManager>;
export const cacheServiceFactory: () => ServiceFactory<
PluginCacheManager,
'plugin'
>;
// @public (undocumented)
export interface ConfigFactoryOptions {
@@ -59,7 +62,7 @@ export interface ConfigFactoryOptions {
// @public (undocumented)
export const configServiceFactory: (
options?: ConfigFactoryOptions | undefined,
) => ServiceFactory<ConfigService>;
) => ServiceFactory<ConfigService, 'root'>;
// @public (undocumented)
export function createConfigSecretEnumerator(options: {
@@ -88,7 +91,10 @@ export interface CreateSpecializedBackendOptions {
}
// @public (undocumented)
export const databaseServiceFactory: () => ServiceFactory<PluginDatabaseManager>;
export const databaseServiceFactory: () => ServiceFactory<
PluginDatabaseManager,
'plugin'
>;
// @public
export class DefaultRootHttpRouter implements RootHttpRouterService {
@@ -106,7 +112,10 @@ export interface DefaultRootHttpRouterOptions {
}
// @public (undocumented)
export const discoveryServiceFactory: () => ServiceFactory<PluginEndpointDiscovery>;
export const discoveryServiceFactory: () => ServiceFactory<
PluginEndpointDiscovery,
'plugin'
>;
// @public
export interface ExtendedHttpServer extends http.Server {
@@ -126,7 +135,7 @@ export interface HttpRouterFactoryOptions {
// @public (undocumented)
export const httpRouterServiceFactory: (
options?: HttpRouterFactoryOptions | undefined,
) => ServiceFactory<HttpRouterService>;
) => ServiceFactory<HttpRouterService, 'plugin'>;
// @public
export type HttpServerCertificateOptions =
@@ -160,10 +169,13 @@ export type IdentityFactoryOptions = {
// @public (undocumented)
export const identityServiceFactory: (
options?: IdentityFactoryOptions | undefined,
) => ServiceFactory<IdentityService>;
) => ServiceFactory<IdentityService, 'plugin'>;
// @public
export const lifecycleServiceFactory: () => ServiceFactory<LifecycleService>;
export const lifecycleServiceFactory: () => ServiceFactory<
LifecycleService,
'plugin'
>;
// @public
export function loadBackendConfig(options: {
@@ -174,7 +186,10 @@ export function loadBackendConfig(options: {
}>;
// @public (undocumented)
export const loggerServiceFactory: () => ServiceFactory<LoggerService>;
export const loggerServiceFactory: () => ServiceFactory<
LoggerService,
'plugin'
>;
// @public
export class MiddlewareFactory {
@@ -202,7 +217,10 @@ export interface MiddlewareFactoryOptions {
}
// @public (undocumented)
export const permissionsServiceFactory: () => ServiceFactory<PermissionsService>;
export const permissionsServiceFactory: () => ServiceFactory<
PermissionsService,
'plugin'
>;
// @public
export function readCorsOptions(config?: Config): CorsOptions;
@@ -238,22 +256,34 @@ export type RootHttpRouterFactoryOptions = {
// @public (undocumented)
export const rootHttpRouterServiceFactory: (
options?: RootHttpRouterFactoryOptions | undefined,
) => ServiceFactory<RootHttpRouterService>;
) => ServiceFactory<RootHttpRouterService, 'root'>;
// @public
export const rootLifecycleServiceFactory: () => ServiceFactory<RootLifecycleService>;
export const rootLifecycleServiceFactory: () => ServiceFactory<
RootLifecycleService,
'root'
>;
// @public (undocumented)
export const rootLoggerServiceFactory: () => ServiceFactory<RootLoggerService>;
export const rootLoggerServiceFactory: () => ServiceFactory<
RootLoggerService,
'root'
>;
// @public (undocumented)
export const schedulerServiceFactory: () => ServiceFactory<SchedulerService>;
export const schedulerServiceFactory: () => ServiceFactory<
SchedulerService,
'plugin'
>;
// @public (undocumented)
export const tokenManagerServiceFactory: () => ServiceFactory<TokenManagerService>;
export const tokenManagerServiceFactory: () => ServiceFactory<
TokenManagerService,
'plugin'
>;
// @public (undocumented)
export const urlReaderServiceFactory: () => ServiceFactory<UrlReader>;
export const urlReaderServiceFactory: () => ServiceFactory<UrlReader, 'plugin'>;
// @public
export class WinstonLogger implements RootLoggerService {
@@ -14,19 +14,12 @@
* limitations under the License.
*/
import {
HttpRouterService,
ServiceFactory,
} from '@backstage/backend-plugin-api';
import { httpRouterServiceFactory } from './httpRouterServiceFactory';
describe('httpRouterFactory', () => {
it('should register plugin paths', async () => {
const rootHttpRouter = { use: jest.fn() };
const factory = httpRouterServiceFactory() as Exclude<
ServiceFactory<HttpRouterService>,
{ scope: 'root' }
>;
const factory = httpRouterServiceFactory() as any;
const handler1 = () => {};
const router1 = await factory.factory(
@@ -57,7 +50,7 @@ describe('httpRouterFactory', () => {
const rootHttpRouter = { use: jest.fn() };
const factory = httpRouterServiceFactory({
getPath: id => `/some/${id}/path`,
}) as Exclude<ServiceFactory<HttpRouterService>, { scope: 'root' }>;
}) as any;
const handler1 = () => {};
const router1 = await factory.factory(
@@ -14,11 +14,7 @@
* limitations under the License.
*/
import {
LoggerService,
ServiceFactory,
TokenManagerService,
} from '@backstage/backend-plugin-api';
import { LoggerService } from '@backstage/backend-plugin-api';
import { ConfigReader } from '@backstage/config';
import { tokenManagerServiceFactory } from './tokenManagerServiceFactory';
@@ -26,10 +22,7 @@ describe('tokenManagerFactory', () => {
it('should create managers that can share tokens in development', async () => {
(process.env as { NODE_ENV?: string }).NODE_ENV = 'development';
const factory = tokenManagerServiceFactory() as Exclude<
ServiceFactory<TokenManagerService>,
{ scope: 'root' }
>;
const factory = tokenManagerServiceFactory() as any;
const deps = {
config: new ConfigReader({}),
logger: { warn() {} } as unknown as LoggerService,
@@ -18,36 +18,66 @@ import {
ServiceFactory,
ServiceRef,
coreServices,
createServiceFactory,
} from '@backstage/backend-plugin-api';
import { stringifyError } from '@backstage/errors';
import { EnumerableServiceHolder } from './types';
// Direct internal import to avoid duplication
// eslint-disable-next-line @backstage/no-forbidden-package-imports
import { InternalServiceFactory } from '@backstage/backend-plugin-api/src/services/system/types';
/**
* Keep in sync with `@backstage/backend-plugin-api/src/services/system/types.ts`
* @internal
*/
export type InternalServiceRef<T> = ServiceRef<T> & {
export type InternalServiceRef = ServiceRef<unknown> & {
__defaultFactory?: (
service: ServiceRef<T>,
) => Promise<ServiceFactory<T> | (() => ServiceFactory<T>)>;
service: ServiceRef<unknown>,
) => Promise<ServiceFactory | (() => ServiceFactory)>;
};
function toInternalServiceFactory<TService, TScope extends 'plugin' | 'root'>(
factory: ServiceFactory<TService, TScope>,
): InternalServiceFactory<TService, TScope> {
const f = factory as InternalServiceFactory<TService, TScope>;
if (f.$$type !== '@backstage/ServiceFactory') {
throw new Error(`Invalid service factory, bad type '${f.$$type}'`);
}
if (f.version !== 'v1') {
throw new Error(`Invalid service factory, bad version '${f.version}'`);
}
return f;
}
const pluginMetadataServiceFactory = createServiceFactory(
(options: { pluginId: string }) => ({
service: coreServices.pluginMetadata,
deps: {},
factory: async () => ({ getId: () => options.pluginId }),
}),
);
export class ServiceRegistry implements EnumerableServiceHolder {
readonly #providedFactories: Map<string, ServiceFactory>;
readonly #loadedDefaultFactories: Map<Function, Promise<ServiceFactory>>;
readonly #providedFactories: Map<string, InternalServiceFactory>;
readonly #loadedDefaultFactories: Map<
Function,
Promise<InternalServiceFactory>
>;
readonly #implementations: Map<
ServiceFactory,
InternalServiceFactory,
{
context: Promise<unknown>;
byPlugin: Map<string, Promise<unknown>>;
}
>;
readonly #rootServiceImplementations = new Map<
ServiceFactory,
InternalServiceFactory,
Promise<unknown>
>();
constructor(factories: Array<ServiceFactory<unknown>>) {
this.#providedFactories = new Map(factories.map(f => [f.service.id, f]));
constructor(factories: Array<ServiceFactory>) {
this.#providedFactories = new Map(
factories.map(sf => [sf.service.id, toInternalServiceFactory(sf)]),
);
this.#loadedDefaultFactories = new Map();
this.#implementations = new Map();
}
@@ -55,23 +85,19 @@ export class ServiceRegistry implements EnumerableServiceHolder {
#resolveFactory(
ref: ServiceRef<unknown>,
pluginId: string,
): Promise<ServiceFactory> | undefined {
): Promise<InternalServiceFactory> | undefined {
// Special case handling of the plugin metadata service, generating a custom factory for it each time
if (ref.id === coreServices.pluginMetadata.id) {
return Promise.resolve<
ServiceFactory<typeof coreServices.pluginMetadata.T>
>({
scope: 'plugin',
service: coreServices.pluginMetadata,
deps: {},
factory: async () => ({ getId: () => pluginId }),
});
return Promise.resolve(
toInternalServiceFactory(pluginMetadataServiceFactory({ pluginId })),
);
}
let resolvedFactory: Promise<ServiceFactory> | ServiceFactory | undefined =
this.#providedFactories.get(ref.id);
const { __defaultFactory: defaultFactory } =
ref as InternalServiceRef<unknown>;
let resolvedFactory:
| Promise<InternalServiceFactory>
| InternalServiceFactory
| undefined = this.#providedFactories.get(ref.id);
const { __defaultFactory: defaultFactory } = ref as InternalServiceRef;
if (!resolvedFactory && !defaultFactory) {
return undefined;
}
@@ -82,8 +108,8 @@ export class ServiceRegistry implements EnumerableServiceHolder {
loadedFactory = Promise.resolve()
.then(() => defaultFactory!(ref))
.then(f =>
typeof f === 'function' ? f() : f,
) as Promise<ServiceFactory>;
toInternalServiceFactory(typeof f === 'function' ? f() : f),
);
this.#loadedDefaultFactories.set(defaultFactory!, loadedFactory);
}
resolvedFactory = loadedFactory.catch(error => {
@@ -100,7 +126,7 @@ export class ServiceRegistry implements EnumerableServiceHolder {
return Promise.resolve(resolvedFactory);
}
#checkForMissingDeps(factory: ServiceFactory, pluginId: string) {
#checkForMissingDeps(factory: InternalServiceFactory, pluginId: string) {
const missingDeps = Object.values(factory.deps).filter(ref => {
if (ref.id === coreServices.pluginMetadata.id) {
return false;
@@ -109,7 +135,7 @@ export class ServiceRegistry implements EnumerableServiceHolder {
return false;
}
return !(ref as InternalServiceRef<unknown>).__defaultFactory;
return !(ref as InternalServiceRef).__defaultFactory;
});
if (missingDeps.length) {
@@ -126,7 +152,7 @@ export class ServiceRegistry implements EnumerableServiceHolder {
get<T>(ref: ServiceRef<T>, pluginId: string): Promise<T> | undefined {
return this.#resolveFactory(ref, pluginId)?.then(factory => {
if (factory.scope === 'root') {
if (factory.service.scope === 'root') {
let existing = this.#rootServiceImplementations.get(factory);
if (!existing) {
this.#checkForMissingDeps(factory, pluginId);
@@ -143,7 +169,7 @@ export class ServiceRegistry implements EnumerableServiceHolder {
}
existing = Promise.all(rootDeps).then(entries =>
factory.factory(Object.fromEntries(entries)),
factory.factory(Object.fromEntries(entries), undefined),
);
this.#rootServiceImplementations.set(factory, existing);
}
+17 -37
View File
@@ -145,7 +145,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: RootServiceFactoryConfig<TService, TImpl, TDeps>,
): () => ServiceFactory<TService>;
): () => ServiceFactory<TService, 'root'>;
// @public
export function createServiceFactory<
@@ -157,7 +157,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: (options?: TOpts) => RootServiceFactoryConfig<TService, TImpl, TDeps>,
): (options?: TOpts) => ServiceFactory<TService>;
): (options?: TOpts) => ServiceFactory<TService, 'root'>;
// @public
export function createServiceFactory<
@@ -169,7 +169,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: (options: TOpts) => RootServiceFactoryConfig<TService, TImpl, TDeps>,
): (options: TOpts) => ServiceFactory<TService>;
): (options: TOpts) => ServiceFactory<TService, 'root'>;
// @public
export function createServiceFactory<
@@ -182,7 +182,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>,
): () => ServiceFactory<TService>;
): () => ServiceFactory<TService, 'plugin'>;
// @public
export function createServiceFactory<
@@ -197,7 +197,7 @@ export function createServiceFactory<
config: (
options?: TOpts,
) => PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>,
): (options?: TOpts) => ServiceFactory<TService>;
): (options?: TOpts) => ServiceFactory<TService, 'plugin'>;
// @public
export function createServiceFactory<
@@ -214,7 +214,7 @@ export function createServiceFactory<
| ((
options: TOpts,
) => PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>),
): (options: TOpts) => ServiceFactory<TService>;
): (options: TOpts) => ServiceFactory<TService, 'plugin'>;
// @public
export function createServiceRef<TService>(
@@ -427,38 +427,18 @@ export type SearchResponseFile = {
};
// @public (undocumented)
export type ServiceFactory<TService = unknown> =
| {
scope: 'root';
service: ServiceRef<TService, 'root'>;
deps: {
[key in string]: ServiceRef<unknown>;
};
factory(deps: {
[key in string]: unknown;
}): Promise<TService>;
}
| {
scope: 'plugin';
service: ServiceRef<TService, 'plugin'>;
deps: {
[key in string]: ServiceRef<unknown>;
};
createRootContext?(deps: {
[key in string]: unknown;
}): Promise<unknown>;
factory(
deps: {
[key in string]: unknown;
},
context: unknown,
): Promise<TService>;
};
export interface ServiceFactory<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
> {
// (undocumented)
$$type: '@backstage/ServiceFactory';
// (undocumented)
service: ServiceRef<TService, TScope>;
}
// @public
export type ServiceFactoryOrFunction<TService = unknown> =
| ServiceFactory<TService>
| (() => ServiceFactory<TService>);
export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory);
// @public
export type ServiceRef<
@@ -477,7 +457,7 @@ export interface ServiceRefConfig<TService, TScope extends 'root' | 'plugin'> {
// (undocumented)
defaultFactory?: (
service: ServiceRef<TService, TScope>,
) => Promise<ServiceFactoryOrFunction<TService>>;
) => Promise<ServiceFactoryOrFunction>;
// (undocumented)
id: string;
// (undocumented)
@@ -48,34 +48,35 @@ export type ServiceRef<
};
/** @public */
export type ServiceFactory<TService = unknown> =
| {
// This scope prop is needed in addition to the service ref, as TypeScript
// can't properly discriminate the two factory types otherwise.
scope: 'root';
service: ServiceRef<TService, 'root'>;
deps: { [key in string]: ServiceRef<unknown> };
factory(deps: { [key in string]: unknown }): Promise<TService>;
}
| {
scope: 'plugin';
service: ServiceRef<TService, 'plugin'>;
deps: { [key in string]: ServiceRef<unknown> };
createRootContext?(deps: { [key in string]: unknown }): Promise<unknown>;
factory(
deps: { [key in string]: unknown },
context: unknown,
): Promise<TService>;
};
export interface ServiceFactory<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
> {
$$type: '@backstage/ServiceFactory';
service: ServiceRef<TService, TScope>;
}
/** @internal */
export interface InternalServiceFactory<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
> extends ServiceFactory<TService, TScope> {
version: 'v1';
deps: { [key in string]: ServiceRef<unknown> };
createRootContext?(deps: { [key in string]: unknown }): Promise<unknown>;
factory(
deps: { [key in string]: unknown },
context: unknown,
): Promise<TService>;
}
/**
* Represents either a {@link ServiceFactory} or a function that returns one.
*
* @public
*/
export type ServiceFactoryOrFunction<TService = unknown> =
| ServiceFactory<TService>
| (() => ServiceFactory<TService>);
export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory);
/** @public */
export interface ServiceRefConfig<TService, TScope extends 'root' | 'plugin'> {
@@ -83,7 +84,7 @@ export interface ServiceRefConfig<TService, TScope extends 'root' | 'plugin'> {
scope?: TScope;
defaultFactory?: (
service: ServiceRef<TService, TScope>,
) => Promise<ServiceFactoryOrFunction<TService>>;
) => Promise<ServiceFactoryOrFunction>;
}
/**
@@ -175,7 +176,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: RootServiceFactoryConfig<TService, TImpl, TDeps>,
): () => ServiceFactory<TService>;
): () => ServiceFactory<TService, 'root'>;
/**
* Creates a root scoped service factory with optional options.
*
@@ -189,7 +190,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: (options?: TOpts) => RootServiceFactoryConfig<TService, TImpl, TDeps>,
): (options?: TOpts) => ServiceFactory<TService>;
): (options?: TOpts) => ServiceFactory<TService, 'root'>;
/**
* Creates a root scoped service factory with required options.
*
@@ -203,7 +204,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: (options: TOpts) => RootServiceFactoryConfig<TService, TImpl, TDeps>,
): (options: TOpts) => ServiceFactory<TService>;
): (options: TOpts) => ServiceFactory<TService, 'root'>;
/**
* Creates a plugin scoped service factory without options.
*
@@ -218,7 +219,7 @@ export function createServiceFactory<
TOpts extends object | undefined = undefined,
>(
config: PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>,
): () => ServiceFactory<TService>;
): () => ServiceFactory<TService, 'plugin'>;
/**
* Creates a plugin scoped service factory with optional options.
*
@@ -235,7 +236,7 @@ export function createServiceFactory<
config: (
options?: TOpts,
) => PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>,
): (options?: TOpts) => ServiceFactory<TService>;
): (options?: TOpts) => ServiceFactory<TService, 'plugin'>;
/**
* Creates a plugin scoped service factory with required options.
*
@@ -254,7 +255,7 @@ export function createServiceFactory<
| ((
options: TOpts,
) => PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>),
): (options: TOpts) => ServiceFactory<TService>;
): (options: TOpts) => ServiceFactory<TService, 'plugin'>;
export function createServiceFactory<
TService,
TImpl extends TService,
@@ -271,20 +272,40 @@ export function createServiceFactory<
) => PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>)
| (() => RootServiceFactoryConfig<TService, TImpl, TDeps>)
| (() => PluginServiceFactoryConfig<TService, TContext, TImpl, TDeps>),
): (options: TOpts) => ServiceFactory<TService> {
): (options: TOpts) => ServiceFactory {
const configCallback = typeof config === 'function' ? config : () => config;
return (options: TOpts) => {
const c = configCallback(options);
return (
options: TOpts,
): InternalServiceFactory<TService, 'plugin' | 'root'> => {
const anyConf = configCallback(options);
if (anyConf.service.scope === 'root') {
const c = anyConf as RootServiceFactoryConfig<TService, TImpl, TDeps>;
return {
$$type: '@backstage/ServiceFactory',
version: 'v1',
service: c.service,
deps: c.deps,
factory: async (deps: TDeps) => c.factory(deps),
};
}
const c = anyConf as PluginServiceFactoryConfig<
TService,
TContext,
TImpl,
TDeps
>;
return {
...c,
$$type: '@backstage/ServiceFactory',
version: 'v1',
service: c.service,
...('createRootContext' in c
? {
createRootContext: async (deps: TDeps) =>
c?.createRootContext?.(deps),
}
: {}),
deps: c.deps,
factory: async (deps: TDeps, ctx: TContext) => c.factory(deps, ctx),
scope: c.service.scope,
} as ServiceFactory<TService>;
};
};
}
+17 -13
View File
@@ -33,7 +33,7 @@ export namespace mockServices {
// (undocumented)
export namespace cache {
const // (undocumented)
factory: () => ServiceFactory<CacheService>;
factory: () => ServiceFactory<CacheService, 'plugin'>;
}
// (undocumented)
export function config(options?: config.Options): ConfigService;
@@ -44,46 +44,48 @@ export namespace mockServices {
data?: JsonObject;
};
const // (undocumented)
factory: (options?: Options | undefined) => ServiceFactory<ConfigService>;
factory: (
options?: Options | undefined,
) => ServiceFactory<ConfigService, 'root'>;
}
// (undocumented)
export namespace database {
const // (undocumented)
factory: () => ServiceFactory<DatabaseService>;
factory: () => ServiceFactory<DatabaseService, 'plugin'>;
}
// (undocumented)
export namespace httpRouter {
const // (undocumented)
factory: (
options?: HttpRouterFactoryOptions | undefined,
) => ServiceFactory<HttpRouterService>;
) => ServiceFactory<HttpRouterService, 'plugin'>;
}
// (undocumented)
export function identity(): IdentityService;
// (undocumented)
export namespace identity {
const // (undocumented)
factory: () => ServiceFactory<IdentityService>;
factory: () => ServiceFactory<IdentityService, 'plugin'>;
}
// (undocumented)
export namespace lifecycle {
const // (undocumented)
factory: () => ServiceFactory<LifecycleService>;
factory: () => ServiceFactory<LifecycleService, 'plugin'>;
}
// (undocumented)
export namespace logger {
const // (undocumented)
factory: () => ServiceFactory<LoggerService>;
factory: () => ServiceFactory<LoggerService, 'plugin'>;
}
// (undocumented)
export namespace permissions {
const // (undocumented)
factory: () => ServiceFactory<PermissionsService>;
factory: () => ServiceFactory<PermissionsService, 'plugin'>;
}
// (undocumented)
export namespace rootLifecycle {
const // (undocumented)
factory: () => ServiceFactory<RootLifecycleService>;
factory: () => ServiceFactory<RootLifecycleService, 'root'>;
}
// (undocumented)
export function rootLogger(options?: rootLogger.Options): LoggerService;
@@ -94,24 +96,26 @@ export namespace mockServices {
level?: 'none' | 'error' | 'warn' | 'info' | 'debug';
};
const // (undocumented)
factory: (options?: Options | undefined) => ServiceFactory<LoggerService>;
factory: (
options?: Options | undefined,
) => ServiceFactory<LoggerService, 'root'>;
}
// (undocumented)
export namespace scheduler {
const // (undocumented)
factory: () => ServiceFactory<SchedulerService>;
factory: () => ServiceFactory<SchedulerService, 'plugin'>;
}
// (undocumented)
export function tokenManager(): TokenManagerService;
// (undocumented)
export namespace tokenManager {
const // (undocumented)
factory: () => ServiceFactory<TokenManagerService>;
factory: () => ServiceFactory<TokenManagerService, 'plugin'>;
}
// (undocumented)
export namespace urlReader {
const // (undocumented)
factory: () => ServiceFactory<UrlReaderService>;
factory: () => ServiceFactory<UrlReaderService, 'plugin'>;
}
}
@@ -40,17 +40,21 @@ import { JsonObject } from '@backstage/types';
import { MockIdentityService } from './MockIdentityService';
import { MockRootLoggerService } from './MockRootLoggerService';
function simpleFactory<TService, TOptions extends [options?: object] = []>(
ref: ServiceRef<TService>,
function simpleFactory<
TService,
TScope extends 'root' | 'plugin',
TOptions extends [options?: object] = [],
>(
ref: ServiceRef<TService, TScope>,
factory: (...options: TOptions) => TService,
): (...options: TOptions) => ServiceFactory<TService> {
): (...options: TOptions) => ServiceFactory<TService, TScope> {
return createServiceFactory((options: unknown) => ({
service: ref as ServiceRef<TService, any>,
deps: {},
async factory() {
return (factory as any)(options);
},
}));
})) as (...options: TOptions) => ServiceFactory<TService, any>;
}
/**