diff --git a/packages/backend-app-api/README.md b/packages/backend-app-api/README.md index 295a04c7a1..ee608ceb43 100644 --- a/packages/backend-app-api/README.md +++ b/packages/backend-app-api/README.md @@ -1,5 +1,7 @@ # @backstage/backend-app-api +**This package is HIGHLY EXPERIMENTAL, do not use this for production** + This package provides the core API used by Backstage backend apps. ## Installation diff --git a/packages/backend-app-api/api-report.md b/packages/backend-app-api/api-report.md index 8c64858310..a37ec6abc6 100644 --- a/packages/backend-app-api/api-report.md +++ b/packages/backend-app-api/api-report.md @@ -6,10 +6,20 @@ import { AnyServiceFactory } from '@backstage/backend-plugin-api'; import { BackendRegistrable } from '@backstage/backend-plugin-api'; -// Warning: (ae-forgotten-export) The symbol "CreateBackendOptions" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "Backend" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "createBackend" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// +// @public (undocumented) +export interface Backend { + // (undocumented) + add(extension: BackendRegistrable): void; + // (undocumented) + start(): Promise; +} + // @public (undocumented) export function createBackend(options?: CreateBackendOptions): Backend; + +// @public (undocumented) +export interface CreateBackendOptions { + // (undocumented) + apis: AnyServiceFactory[]; +} ``` diff --git a/packages/backend-app-api/src/index.ts b/packages/backend-app-api/src/index.ts index 33aca2b291..c58379c3e4 100644 --- a/packages/backend-app-api/src/index.ts +++ b/packages/backend-app-api/src/index.ts @@ -20,4 +20,4 @@ * @packageDocumentation */ -export { createBackend } from './wiring/types'; +export * from './wiring'; diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index d33427d54d..ab086f4144 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -14,31 +14,38 @@ * limitations under the License. */ -import { BackendRegistrable, ServiceRef } from '@backstage/backend-plugin-api'; -import { BackendRegisterInit, ApiHolder } from './types'; +import { + BackendRegistrable, + ExtensionPoint, + ServiceRef, +} from '@backstage/backend-plugin-api'; +import { BackendRegisterInit, ServiceHolder } from './types'; + +type ServiceOrExtensionPoint = ExtensionPoint | ServiceRef; export class BackendInitializer { #started = false; #extensions = new Map(); - // #stops = []; #registerInits = new Array(); - #apis = new Map, unknown>(); - #apiHolder: ApiHolder; + #extensionPoints = new Map(); + #serviceHolder: ServiceHolder; - constructor(apiHolder: ApiHolder) { - this.#apiHolder = apiHolder; + constructor(serviceHolder: ServiceHolder) { + this.#serviceHolder = serviceHolder; } async #getInitDeps( - deps: { [name: string]: ServiceRef }, + deps: { [name: string]: ServiceOrExtensionPoint }, pluginId: string, ) { return Object.fromEntries( await Promise.all( - Object.entries(deps).map(async ([name, apiRef]) => [ + Object.entries(deps).map(async ([name, ref]) => [ name, - this.#apis.get(apiRef) || - (await this.#apiHolder.get(apiRef)!(pluginId)), + this.#extensionPoints.get(ref) || + (await this.#serviceHolder.get(ref as ServiceRef)!( + pluginId, + )), ]), ), ); @@ -67,15 +74,15 @@ export class BackendInitializer { console.log('Registering', extension.id); extension.register({ - registerExtensionPoint: (api, impl) => { + registerExtensionPoint: (extensionPointRef, impl) => { if (registerInit) { throw new Error('registerExtensionPoint called after registerInit'); } - if (this.#apis.has(api)) { - throw new Error(`API ${api.id} already registered`); + if (this.#extensionPoints.has(extensionPointRef)) { + throw new Error(`API ${extensionPointRef.id} already registered`); } - this.#apis.set(api, impl); - provides.add(api); + this.#extensionPoints.set(extensionPointRef, impl); + provides.add(extensionPointRef); }, registerInit: registerOptions => { if (registerInit) { @@ -105,20 +112,11 @@ export class BackendInitializer { const orderedRegisterResults = this.#resolveInitOrder(this.#registerInits); for (const registerInit of orderedRegisterResults) { - // TODO: DI const deps = await this.#getInitDeps(registerInit.deps, registerInit.id); await registerInit.init(deps); - // Maybe return stop? or lifecycle API - // this.#stops.push(); } } - // async stop(): Promise { - // for (const stop of this.#stops) { - // await stop.stop(); - // } - // } - private validateSetup() {} #resolveInitOrder(registerInits: Array) { @@ -133,13 +131,13 @@ export class BackendInitializer { for (const registerInit of registerInitsToOrder) { const unInitializedDependents = []; - for (const api of registerInit.provides) { + for (const serviceRef of registerInit.provides) { if ( registerInitsToOrder.some( - init => init !== registerInit && init.consumes.has(api), + init => init !== registerInit && init.consumes.has(serviceRef), ) ) { - unInitializedDependents.push(api); + unInitializedDependents.push(serviceRef); } } diff --git a/packages/backend-app-api/src/wiring/BackstageBackend.ts b/packages/backend-app-api/src/wiring/BackstageBackend.ts index 69df1fdf82..104d2b6fc8 100644 --- a/packages/backend-app-api/src/wiring/BackstageBackend.ts +++ b/packages/backend-app-api/src/wiring/BackstageBackend.ts @@ -19,16 +19,16 @@ import { BackendRegistrable, } from '@backstage/backend-plugin-api'; import { BackendInitializer } from './BackendInitializer'; -import { CoreApiRegistry } from './CoreApiRegistry'; +import { ServiceRegistry } from './ServiceRegistry'; import { Backend } from './types'; export class BackstageBackend implements Backend { - #coreApis: CoreApiRegistry; + #services: ServiceRegistry; #initializer: BackendInitializer; constructor(apiFactories: AnyServiceFactory[]) { - this.#coreApis = new CoreApiRegistry(apiFactories); - this.#initializer = new BackendInitializer(this.#coreApis); + this.#services = new ServiceRegistry(apiFactories); + this.#initializer = new BackendInitializer(this.#services); } add(extension: BackendRegistrable): void { diff --git a/packages/backend-app-api/src/wiring/CoreApiRegistry.ts b/packages/backend-app-api/src/wiring/ServiceRegistry.ts similarity index 71% rename from packages/backend-app-api/src/wiring/CoreApiRegistry.ts rename to packages/backend-app-api/src/wiring/ServiceRegistry.ts index 4b1e0f5ce0..bdaecf30c6 100644 --- a/packages/backend-app-api/src/wiring/CoreApiRegistry.ts +++ b/packages/backend-app-api/src/wiring/ServiceRegistry.ts @@ -19,7 +19,7 @@ import { ServiceRef, } from '@backstage/backend-plugin-api'; -export class CoreApiRegistry { +export class ServiceRegistry { readonly #implementations: Map>; readonly #factories: Map; @@ -35,29 +35,27 @@ export class CoreApiRegistry { } return async (pluginId: string): Promise => { - if (this.#implementations.has(ref.id)) { - if (this.#implementations.get(ref.id)!.has(pluginId)) { - return this.#implementations.get(ref.id)!.get(pluginId) as T; + let implementations = this.#implementations.get(ref.id); + if (implementations) { + if (implementations.has(pluginId)) { + return implementations.get(pluginId) as T; } - this.#implementations.set(ref.id, new Map()); } else { - this.#implementations.set(ref.id, new Map()); + implementations = new Map(); + this.#implementations.set(ref.id, implementations); } const factoryDeps = Object.fromEntries( - Object.entries(factory.deps).map(([name, apiRef]) => [ + Object.entries(factory.deps).map(([name, serviceRef]) => [ name, - this.get(apiRef)!, // TODO: throw + this.get(serviceRef)!, // TODO: throw ]), ); const factoryFunc = await factory.factory(factoryDeps); const implementation = await factoryFunc(pluginId); - this.#implementations.set( - ref.id, - this.#implementations.get(ref.id)!.set(pluginId, implementation), - ); + implementations.set(pluginId, implementation); return implementation as T; }; diff --git a/packages/backend-app-api/src/wiring/index.ts b/packages/backend-app-api/src/wiring/index.ts new file mode 100644 index 0000000000..1dd91e30e1 --- /dev/null +++ b/packages/backend-app-api/src/wiring/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type { Backend, CreateBackendOptions } from './types'; +export { createBackend } from './types'; diff --git a/packages/backend-app-api/src/wiring/types.ts b/packages/backend-app-api/src/wiring/types.ts index c1c43a8ba8..112fe06b05 100644 --- a/packages/backend-app-api/src/wiring/types.ts +++ b/packages/backend-app-api/src/wiring/types.ts @@ -23,6 +23,9 @@ import { import { defaultServiceFactories } from '../services/implementations'; import { BackstageBackend } from './BackstageBackend'; +/** + * @public + */ export interface Backend { add(extension: BackendRegistrable): void; start(): Promise; @@ -36,14 +39,20 @@ export interface BackendRegisterInit { init: (deps: { [name: string]: unknown }) => Promise; } -interface CreateBackendOptions { +/** + * @public + */ +export interface CreateBackendOptions { apis: AnyServiceFactory[]; } -export type ApiHolder = { +export type ServiceHolder = { get(api: ServiceRef): FactoryFunc | undefined; }; +/** + * @public + */ export function createBackend(options?: CreateBackendOptions): Backend { // TODO: merge with provided APIs return new BackstageBackend([ diff --git a/packages/backend-plugin-api/README.md b/packages/backend-plugin-api/README.md index 9e28e220a1..364655dd60 100644 --- a/packages/backend-plugin-api/README.md +++ b/packages/backend-plugin-api/README.md @@ -1,5 +1,7 @@ # @backstage/backend-plugin-api +**This package is HIGHLY EXPERIMENTAL, do not use this for production** + This package provides the core API used by Backstage backend plugins. ## Installation