backend-app-api: always initialize root services

Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Johan Haals <johan.haals@gmail.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-12-14 14:12:34 +01:00
parent b8aa070dc4
commit 5260d8fc7d
5 changed files with 88 additions and 7 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-app-api': patch
---
Root scoped services are now always initialized, regardless of whether they're used by any features.
@@ -0,0 +1,57 @@
/*
* 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.
*/
import {
createServiceRef,
createServiceFactory,
} from '@backstage/backend-plugin-api';
import { BackendInitializer } from './BackendInitializer';
import { ServiceRegistry } from './ServiceRegistry';
const rootRef = createServiceRef<{ x: number }>({
id: '1',
scope: 'root',
});
const pluginRef = createServiceRef<{ x: number }>({
id: '2',
});
describe('BackendInitializer', () => {
it('should initialize root scoped services', async () => {
const rootFactory = jest.fn();
const pluginFactory = jest.fn();
const registry = new ServiceRegistry([
createServiceFactory({
service: rootRef,
deps: {},
factory: rootFactory,
}),
createServiceFactory({
service: pluginRef,
deps: {},
factory: pluginFactory,
}),
]);
const init = new BackendInitializer(registry);
await init.start();
expect(rootFactory).toHaveBeenCalled();
expect(pluginFactory).not.toHaveBeenCalled();
});
});
@@ -21,7 +21,7 @@ import {
} from '@backstage/backend-plugin-api';
import {
BackendRegisterInit,
ServiceHolder,
EnumerableServiceHolder,
ServiceOrExtensionPoint,
} from './types';
@@ -30,9 +30,9 @@ export class BackendInitializer {
#features = new Map<BackendFeature, unknown>();
#registerInits = new Array<BackendRegisterInit>();
#extensionPoints = new Map<ExtensionPoint<unknown>, unknown>();
#serviceHolder: ServiceHolder;
#serviceHolder: EnumerableServiceHolder;
constructor(serviceHolder: ServiceHolder) {
constructor(serviceHolder: EnumerableServiceHolder) {
this.#serviceHolder = serviceHolder;
}
@@ -85,6 +85,14 @@ export class BackendInitializer {
}
this.#started = true;
// Initialize all root scoped services
for (const ref of this.#serviceHolder.getServiceRefs()) {
if (ref.scope === 'root') {
await this.#serviceHolder.get(ref, 'root');
}
}
// Initialize all features
for (const [feature] of this.#features) {
const provides = new Set<ExtensionPoint<unknown>>();
@@ -20,7 +20,7 @@ import {
coreServices,
} from '@backstage/backend-plugin-api';
import { stringifyError } from '@backstage/errors';
import { EnumerableServiceHolder } from './types';
/**
* Keep in sync with `@backstage/backend-plugin-api/src/services/system/types.ts`
* @internal
@@ -31,7 +31,7 @@ export type InternalServiceRef<T> = ServiceRef<T> & {
) => Promise<ServiceFactory<T> | (() => ServiceFactory<T>)>;
};
export class ServiceRegistry {
export class ServiceRegistry implements EnumerableServiceHolder {
readonly #providedFactories: Map<string, ServiceFactory>;
readonly #loadedDefaultFactories: Map<Function, Promise<ServiceFactory>>;
readonly #implementations: Map<
@@ -132,6 +132,10 @@ export class ServiceRegistry {
}
}
getServiceRefs(): ServiceRef<unknown>[] {
return Array.from(this.#providedFactories.values()).map(f => f.service);
}
get<T>(ref: ServiceRef<T>, pluginId: string): Promise<T> | undefined {
return this.#resolveFactory(ref, pluginId)?.then(factory => {
if (factory.scope === 'root') {
+9 -2
View File
@@ -45,9 +45,16 @@ export interface CreateSpecializedBackendOptions {
services: (ServiceFactory | (() => ServiceFactory))[];
}
export type ServiceHolder = {
export interface ServiceHolder {
get<T>(api: ServiceRef<T>, pluginId: string): Promise<T> | undefined;
};
}
/**
* @internal
*/
export interface EnumerableServiceHolder extends ServiceHolder {
getServiceRefs(): ServiceRef<unknown>[];
}
/**
* @public