backend-plugin-api: make ServiceFactory opaque
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Updated usage of `ServiceFactory`.
|
||||
@@ -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.
|
||||
@@ -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 {
|
||||
|
||||
+2
-9
@@ -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(
|
||||
|
||||
+2
-9
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user