backend-app-api: keep track of the provided services
Signed-off-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
committed by
Patrik Oldsberg
parent
263f6be4f6
commit
6e7a1b07f3
@@ -23,7 +23,7 @@ import {
|
||||
createExtensionPoint,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { BackendInitializer } from './BackendInitializer';
|
||||
import { ServiceRegistry } from './ServiceRegistry';
|
||||
|
||||
import {
|
||||
lifecycleServiceFactory,
|
||||
loggerServiceFactory,
|
||||
@@ -65,7 +65,7 @@ describe('BackendInitializer', () => {
|
||||
const rootFactory = jest.fn();
|
||||
const pluginFactory = jest.fn();
|
||||
|
||||
const registry = new ServiceRegistry([
|
||||
const services = [
|
||||
createServiceFactory({
|
||||
service: rootRef,
|
||||
deps: {},
|
||||
@@ -82,9 +82,9 @@ describe('BackendInitializer', () => {
|
||||
deps: {},
|
||||
factory: () => new MockLogger(),
|
||||
})(),
|
||||
]);
|
||||
];
|
||||
|
||||
const init = new BackendInitializer(registry);
|
||||
const init = new BackendInitializer(services);
|
||||
await init.start();
|
||||
|
||||
expect(rootFactory).toHaveBeenCalled();
|
||||
@@ -97,7 +97,7 @@ describe('BackendInitializer', () => {
|
||||
const extensionPoint = createExtensionPoint<{ values: string[] }>({
|
||||
id: 'a',
|
||||
});
|
||||
const init = new BackendInitializer(new ServiceRegistry(baseFactories));
|
||||
const init = new BackendInitializer(baseFactories);
|
||||
|
||||
init.add(
|
||||
createBackendModule({
|
||||
@@ -151,7 +151,7 @@ describe('BackendInitializer', () => {
|
||||
});
|
||||
|
||||
it('should forward errors when plugins fail to start', async () => {
|
||||
const init = new BackendInitializer(new ServiceRegistry([]));
|
||||
const init = new BackendInitializer([]);
|
||||
init.add(
|
||||
createBackendPlugin({
|
||||
pluginId: 'test',
|
||||
@@ -171,7 +171,7 @@ describe('BackendInitializer', () => {
|
||||
});
|
||||
|
||||
it('should forward errors when modules fail to start', async () => {
|
||||
const init = new BackendInitializer(new ServiceRegistry([]));
|
||||
const init = new BackendInitializer([]);
|
||||
init.add(
|
||||
createBackendModule({
|
||||
pluginId: 'test',
|
||||
@@ -192,7 +192,7 @@ describe('BackendInitializer', () => {
|
||||
});
|
||||
|
||||
it('should reject duplicate plugins', async () => {
|
||||
const init = new BackendInitializer(new ServiceRegistry([]));
|
||||
const init = new BackendInitializer([]);
|
||||
init.add(
|
||||
createBackendPlugin({
|
||||
pluginId: 'test',
|
||||
@@ -221,7 +221,7 @@ describe('BackendInitializer', () => {
|
||||
});
|
||||
|
||||
it('should reject duplicate modules', async () => {
|
||||
const init = new BackendInitializer(new ServiceRegistry([]));
|
||||
const init = new BackendInitializer([]);
|
||||
init.add(
|
||||
createBackendModule({
|
||||
pluginId: 'test',
|
||||
@@ -254,16 +254,14 @@ describe('BackendInitializer', () => {
|
||||
it('should reject modules with circular dependencies', async () => {
|
||||
const extA = createExtensionPoint<string>({ id: 'a' });
|
||||
const extB = createExtensionPoint<string>({ id: 'b' });
|
||||
const init = new BackendInitializer(
|
||||
new ServiceRegistry([
|
||||
rootLifecycleServiceFactory(),
|
||||
createServiceFactory({
|
||||
service: coreServices.rootLogger,
|
||||
deps: {},
|
||||
factory: () => new MockLogger(),
|
||||
})(),
|
||||
]),
|
||||
);
|
||||
const init = new BackendInitializer([
|
||||
rootLifecycleServiceFactory(),
|
||||
createServiceFactory({
|
||||
service: coreServices.rootLogger,
|
||||
deps: {},
|
||||
factory: () => new MockLogger(),
|
||||
})(),
|
||||
]);
|
||||
init.add(
|
||||
createBackendModule({
|
||||
pluginId: 'test',
|
||||
@@ -296,7 +294,7 @@ describe('BackendInitializer', () => {
|
||||
});
|
||||
|
||||
it('should reject modules that depend on extension points other plugins', async () => {
|
||||
const init = new BackendInitializer(new ServiceRegistry(baseFactories));
|
||||
const init = new BackendInitializer(baseFactories);
|
||||
const extA = createExtensionPoint<string>({ id: 'a' });
|
||||
init.add(
|
||||
createBackendPlugin({
|
||||
|
||||
@@ -30,6 +30,7 @@ import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring
|
||||
import { ForwardedError, ConflictError } from '@backstage/errors';
|
||||
import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha';
|
||||
import { DependencyGraph } from '../lib/DependencyGraph';
|
||||
import { ServiceRegistry } from './ServiceRegistry';
|
||||
|
||||
export interface BackendRegisterInit {
|
||||
consumes: Set<ServiceOrExtensionPoint>;
|
||||
@@ -47,10 +48,12 @@ export class BackendInitializer {
|
||||
ExtensionPoint<unknown>,
|
||||
{ impl: unknown; pluginId: string }
|
||||
>();
|
||||
#serviceHolder: EnumerableServiceHolder;
|
||||
#serviceHolder: EnumerableServiceHolder | undefined;
|
||||
#providedServiceFactories = new Array<ServiceFactory>();
|
||||
#defaultApiFactories: ServiceFactory[];
|
||||
|
||||
constructor(serviceHolder: EnumerableServiceHolder) {
|
||||
this.#serviceHolder = serviceHolder;
|
||||
constructor(defaultApiFactories: ServiceFactory[]) {
|
||||
this.#defaultApiFactories = defaultApiFactories;
|
||||
}
|
||||
|
||||
async #getInitDeps(
|
||||
@@ -70,7 +73,7 @@ export class BackendInitializer {
|
||||
}
|
||||
result.set(name, ep.impl);
|
||||
} else {
|
||||
const impl = await this.#serviceHolder.get(
|
||||
const impl = await this.#serviceHolder!.get(
|
||||
ref as ServiceRef<unknown>,
|
||||
pluginId,
|
||||
);
|
||||
@@ -112,7 +115,16 @@ export class BackendInitializer {
|
||||
`The ${coreServices.pluginMetadata.id} service cannot be overridden`,
|
||||
);
|
||||
}
|
||||
this.#serviceHolder.add(feature);
|
||||
if (
|
||||
this.#providedServiceFactories.find(
|
||||
sf => sf.service.id === feature.service.id,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Duplicate service implementations provided for ${feature.service.id}`,
|
||||
);
|
||||
}
|
||||
this.#providedServiceFactories.push(feature);
|
||||
} else if (isInternalBackendFeature(feature)) {
|
||||
if (feature.version !== 'v1') {
|
||||
throw new Error(
|
||||
@@ -155,6 +167,8 @@ export class BackendInitializer {
|
||||
}
|
||||
|
||||
async #doStart(): Promise<void> {
|
||||
this.#serviceHolder = new ServiceRegistry(this.#defaultApiFactories);
|
||||
|
||||
const featureDiscovery = await this.#serviceHolder.get(
|
||||
featureDiscoveryServiceRef,
|
||||
'root',
|
||||
@@ -333,7 +347,7 @@ export class BackendInitializer {
|
||||
|
||||
// Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this
|
||||
async #getRootLifecycleImpl(): Promise<BackendLifecycleImpl> {
|
||||
const lifecycleService = await this.#serviceHolder.get(
|
||||
const lifecycleService = await this.#serviceHolder!.get(
|
||||
coreServices.rootLifecycle,
|
||||
'root',
|
||||
);
|
||||
@@ -346,7 +360,7 @@ export class BackendInitializer {
|
||||
async #getPluginLifecycleImpl(
|
||||
pluginId: string,
|
||||
): Promise<BackendPluginLifecycleImpl> {
|
||||
const lifecycleService = await this.#serviceHolder.get(
|
||||
const lifecycleService = await this.#serviceHolder!.get(
|
||||
coreServices.lifecycle,
|
||||
pluginId,
|
||||
);
|
||||
|
||||
@@ -14,18 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ServiceFactory, BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { BackendFeature, ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
import { BackendInitializer } from './BackendInitializer';
|
||||
import { ServiceRegistry } from './ServiceRegistry';
|
||||
import { Backend } from './types';
|
||||
|
||||
export class BackstageBackend implements Backend {
|
||||
#services: ServiceRegistry;
|
||||
#initializer: BackendInitializer;
|
||||
|
||||
constructor(apiFactories: ServiceFactory[]) {
|
||||
this.#services = new ServiceRegistry(apiFactories);
|
||||
this.#initializer = new BackendInitializer(this.#services);
|
||||
constructor(defaultServiceFactories: ServiceFactory[]) {
|
||||
this.#initializer = new BackendInitializer(defaultServiceFactories);
|
||||
}
|
||||
|
||||
add(feature: BackendFeature | (() => BackendFeature)): void {
|
||||
|
||||
Reference in New Issue
Block a user