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:
@@ -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') {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user