backend-app-api: fixes, renames, cleanup
Co-authored-by: Johan Haals <johan.haals@gmail.com> Co-authored-by: Fredrik Adelöw <freben@gmail.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function createBackend(options?: CreateBackendOptions): Backend;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CreateBackendOptions {
|
||||
// (undocumented)
|
||||
apis: AnyServiceFactory[];
|
||||
}
|
||||
```
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export { createBackend } from './wiring/types';
|
||||
export * from './wiring';
|
||||
|
||||
@@ -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<unknown> | ServiceRef<unknown>;
|
||||
|
||||
export class BackendInitializer {
|
||||
#started = false;
|
||||
#extensions = new Map<BackendRegistrable, unknown>();
|
||||
// #stops = [];
|
||||
#registerInits = new Array<BackendRegisterInit>();
|
||||
#apis = new Map<ServiceRef<unknown>, unknown>();
|
||||
#apiHolder: ApiHolder;
|
||||
#extensionPoints = new Map<ServiceOrExtensionPoint, unknown>();
|
||||
#serviceHolder: ServiceHolder;
|
||||
|
||||
constructor(apiHolder: ApiHolder) {
|
||||
this.#apiHolder = apiHolder;
|
||||
constructor(serviceHolder: ServiceHolder) {
|
||||
this.#serviceHolder = serviceHolder;
|
||||
}
|
||||
|
||||
async #getInitDeps(
|
||||
deps: { [name: string]: ServiceRef<unknown> },
|
||||
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<unknown>)!(
|
||||
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<void> {
|
||||
// for (const stop of this.#stops) {
|
||||
// await stop.stop();
|
||||
// }
|
||||
// }
|
||||
|
||||
private validateSetup() {}
|
||||
|
||||
#resolveInitOrder(registerInits: Array<BackendRegisterInit>) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+10
-12
@@ -19,7 +19,7 @@ import {
|
||||
ServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
export class CoreApiRegistry {
|
||||
export class ServiceRegistry {
|
||||
readonly #implementations: Map<string, Map<string, unknown>>;
|
||||
readonly #factories: Map<string, AnyServiceFactory>;
|
||||
|
||||
@@ -35,29 +35,27 @@ export class CoreApiRegistry {
|
||||
}
|
||||
|
||||
return async (pluginId: string): Promise<T> => {
|
||||
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<string, unknown>());
|
||||
} 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;
|
||||
};
|
||||
@@ -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';
|
||||
@@ -23,6 +23,9 @@ import {
|
||||
import { defaultServiceFactories } from '../services/implementations';
|
||||
import { BackstageBackend } from './BackstageBackend';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Backend {
|
||||
add(extension: BackendRegistrable): void;
|
||||
start(): Promise<void>;
|
||||
@@ -36,14 +39,20 @@ export interface BackendRegisterInit {
|
||||
init: (deps: { [name: string]: unknown }) => Promise<void>;
|
||||
}
|
||||
|
||||
interface CreateBackendOptions {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CreateBackendOptions {
|
||||
apis: AnyServiceFactory[];
|
||||
}
|
||||
|
||||
export type ApiHolder = {
|
||||
export type ServiceHolder = {
|
||||
get<T>(api: ServiceRef<T>): FactoryFunc<T> | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function createBackend(options?: CreateBackendOptions): Backend {
|
||||
// TODO: merge with provided APIs
|
||||
return new BackstageBackend([
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user