refactor: convert discovery services into feature loaders

Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-08-26 09:42:47 +02:00
parent 123537d356
commit cd38da88ba
13 changed files with 157 additions and 46 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-app-api': patch
---
Deprecate the `featureDiscoveryServiceFactory` in favor of using `featureDiscoveryLoader` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-dynamic-feature-service': patch
---
Deprecate the `dynamicPluginsServiceRef`, `dynamicPluginsServiceFactory` and `dynamicPluginsServiceFactoryWithOptions` in favor of using `dynamicPluginsFeatureDiscoveryLoader` or `dynamicPluginsFeatureDiscoveryLoaderWithOptions` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-plugin-api': patch
---
Deprecate the `featureDiscoveryServiceRef` in favor of using `@backstage/backend-app-api#featureDiscoveryLoader` instead.
+5 -1
View File
@@ -3,10 +3,14 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { BackendFeature } from '@backstage/backend-plugin-api';
import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';
import { ServiceFactory } from '@backstage/backend-plugin-api';
// @alpha (undocumented)
// @public
export const featureDiscoveryLoader: BackendFeature;
// @alpha @deprecated (undocumented)
export const featureDiscoveryServiceFactory: ServiceFactory<
FeatureDiscoveryService,
'root',
+4 -1
View File
@@ -14,4 +14,7 @@
* limitations under the License.
*/
export { featureDiscoveryServiceFactory } from './alpha/featureDiscoveryServiceFactory';
export {
featureDiscoveryLoader,
featureDiscoveryServiceFactory,
} from './alpha/featureDiscoveryServiceFactory';
@@ -19,6 +19,7 @@ import {
RootConfigService,
RootLoggerService,
coreServices,
createBackendFeatureLoader,
createServiceFactory,
} from '@backstage/backend-plugin-api';
import {
@@ -153,7 +154,10 @@ class PackageDiscoveryService implements FeatureDiscoveryService {
}
}
/** @alpha */
/**
* @alpha
* @deprecated The `featureDiscoveryServiceFactory` is deprecated in favor of using {@link featureDiscoveryLoader} instead.
*/
export const featureDiscoveryServiceFactory = createServiceFactory({
service: featureDiscoveryServiceRef,
deps: {
@@ -165,6 +169,36 @@ export const featureDiscoveryServiceFactory = createServiceFactory({
},
});
/**
* A loader that discovers backend features from the current package.json and its dependencies.
*
* @public
*
* @example
* Using the `featureDiscoveryLoader` loader in a backend instance:
* ```ts
* //...
* import { createBackend } from '@backstage/backend-defaults';
* import { featureDiscoveryLoader } from '@backstage/backend-app-api';
*
* const backend = createBackend();
* backend.add(featureDiscoveryLoader);
* //...
* backend.start();
* ```
*/
export const featureDiscoveryLoader = createBackendFeatureLoader({
deps: {
config: coreServices.rootConfig,
logger: coreServices.rootLogger,
},
async loader({ config, logger }) {
const service = new PackageDiscoveryService(config, logger);
const { features } = await service.getBackendFeatures();
return features;
},
});
function isBackendFeature(value: unknown): value is BackendFeature {
return (
!!value &&
@@ -20,32 +20,23 @@ import { loggerServiceFactory } from '@backstage/backend-defaults/logger';
import {
createServiceRef,
createServiceFactory,
coreServices,
createBackendPlugin,
createBackendModule,
createExtensionPoint,
createBackendFeatureLoader,
} from '@backstage/backend-plugin-api';
import { BackendInitializer } from './BackendInitializer';
import { mockServices } from '@backstage/backend-test-utils';
class MockLogger {
debug() {}
info() {}
warn() {}
error() {}
child() {
return this;
}
}
const requiredRootFactories = [
mockServices.rootConfig.factory(),
mockServices.rootLogger.factory(),
];
const baseFactories = [
...requiredRootFactories,
lifecycleServiceFactory,
rootLifecycleServiceFactory,
createServiceFactory({
service: coreServices.rootLogger,
deps: {},
factory: () => new MockLogger(),
}),
loggerServiceFactory,
];
@@ -399,7 +390,7 @@ describe('BackendInitializer', () => {
});
it('should forward errors when plugins fail to start', async () => {
const init = new BackendInitializer([]);
const init = new BackendInitializer(requiredRootFactories);
init.add(
createBackendPlugin({
pluginId: 'test',
@@ -419,7 +410,7 @@ describe('BackendInitializer', () => {
});
it('should forward errors when modules fail to start', async () => {
const init = new BackendInitializer([]);
const init = new BackendInitializer(requiredRootFactories);
init.add(testPlugin);
init.add(
createBackendModule({
@@ -441,7 +432,7 @@ describe('BackendInitializer', () => {
});
it('should reject duplicate plugins', async () => {
const init = new BackendInitializer([]);
const init = new BackendInitializer(requiredRootFactories);
init.add(
createBackendPlugin({
pluginId: 'test',
@@ -470,7 +461,7 @@ describe('BackendInitializer', () => {
});
it('should reject duplicate modules', async () => {
const init = new BackendInitializer([]);
const init = new BackendInitializer(requiredRootFactories);
init.add(testPlugin);
init.add(
createBackendModule({
@@ -505,12 +496,8 @@ describe('BackendInitializer', () => {
const extA = createExtensionPoint<string>({ id: 'a' });
const extB = createExtensionPoint<string>({ id: 'b' });
const init = new BackendInitializer([
...requiredRootFactories,
rootLifecycleServiceFactory,
createServiceFactory({
service: coreServices.rootLogger,
deps: {},
factory: () => new MockLogger(),
}),
]);
init.add(testPlugin);
init.add(
@@ -34,11 +34,11 @@ import type {
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';
import { ForwardedError, ConflictError } from '@backstage/errors';
import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha';
import { DependencyGraph } from '../lib/DependencyGraph';
import { ServiceRegistry } from './ServiceRegistry';
import { createInitializationLogger } from './createInitializationLogger';
import { unwrapFeature } from './helpers';
import { featureDiscoveryLoader } from '../alpha/featureDiscoveryServiceFactory';
export interface BackendRegisterInit {
consumes: Set<ServiceOrExtensionPoint>;
@@ -150,25 +150,14 @@ export class BackendInitializer {
}
async #doStart(): Promise<void> {
this.add(featureDiscoveryLoader);
this.#serviceRegistry.checkForCircularDeps();
for (const feature of this.#registeredFeatures) {
this.#addFeature(await feature);
}
const featureDiscovery = await this.#serviceRegistry.get(
featureDiscoveryServiceRef,
'root',
);
if (featureDiscovery) {
const { features } = await featureDiscovery.getBackendFeatures();
for (const feature of features) {
this.#addFeature(unwrapFeature(feature));
}
this.#serviceRegistry.checkForCircularDeps();
}
await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders);
// Initialize all root scoped services
@@ -112,7 +112,15 @@ export interface DynamicPluginsFactoryOptions {
moduleLoader?(logger: LoggerService): ModuleLoader;
}
// @public (undocumented)
// @public
export const dynamicPluginsFeatureDiscoveryLoader: BackendFeature;
// @public
export const dynamicPluginsFeatureDiscoveryLoaderWithOptions: (
options?: DynamicPluginsFactoryOptions,
) => BackendFeature;
// @public @deprecated (undocumented)
export const dynamicPluginsFeatureDiscoveryServiceFactory: ServiceFactory<
FeatureDiscoveryService,
'root',
@@ -154,19 +162,19 @@ export const dynamicPluginsSchemasServiceFactoryWithOptions: (
options?: DynamicPluginsSchemasOptions,
) => ServiceFactory<DynamicPluginsSchemasService, 'root', 'singleton'>;
// @public (undocumented)
// @public @deprecated (undocumented)
export const dynamicPluginsServiceFactory: ServiceFactory<
DynamicPluginProvider,
'root',
'singleton'
>;
// @public (undocumented)
// @public @deprecated (undocumented)
export const dynamicPluginsServiceFactoryWithOptions: (
options?: DynamicPluginsFactoryOptions,
) => ServiceFactory<DynamicPluginProvider, 'root', 'singleton'>;
// @public (undocumented)
// @public @deprecated (undocumented)
export const dynamicPluginsServiceRef: ServiceRef<
DynamicPluginProvider,
'root',
@@ -36,6 +36,8 @@ export {
dynamicPluginsServiceFactory,
dynamicPluginsServiceFactoryWithOptions,
dynamicPluginsServiceRef,
dynamicPluginsFeatureDiscoveryLoader,
dynamicPluginsFeatureDiscoveryLoaderWithOptions,
} from './plugin-manager';
export type {
@@ -30,6 +30,7 @@ import {
BackendFeature,
LoggerService,
coreServices,
createBackendFeatureLoader,
createServiceFactory,
createServiceRef,
} from '@backstage/backend-plugin-api';
@@ -214,6 +215,7 @@ export class DynamicPluginManager implements DynamicPluginProvider {
/**
* @public
* @deprecated The `featureDiscoveryService` is deprecated in favor of using {@link dynamicPluginsFeatureDiscoveryLoader} or {@link dynamicPluginsFeatureDiscoveryLoaderWithOptions} instead.
*/
export const dynamicPluginsServiceRef = createServiceRef<DynamicPluginProvider>(
{
@@ -231,6 +233,7 @@ export interface DynamicPluginsFactoryOptions {
/**
* @public
* @deprecated Use {@link dynamicPluginsFeatureDiscoveryLoaderWithOptions} instead.
*/
export const dynamicPluginsServiceFactoryWithOptions = (
options?: DynamicPluginsFactoryOptions,
@@ -253,6 +256,7 @@ export const dynamicPluginsServiceFactoryWithOptions = (
/**
* @public
* @deprecated Use {@link dynamicPluginsFeatureDiscoveryLoader} instead.
*/
export const dynamicPluginsServiceFactory =
dynamicPluginsServiceFactoryWithOptions();
@@ -292,6 +296,7 @@ class DynamicPluginsEnabledFeatureDiscoveryService
/**
* @public
* @deprecated The `featureDiscoveryService` is deprecated in favor of using {@link dynamicPluginsFeatureDiscoveryLoader} or {@link dynamicPluginsFeatureDiscoveryLoaderWithOptions} instead.
*/
export const dynamicPluginsFeatureDiscoveryServiceFactory =
createServiceFactory({
@@ -305,6 +310,69 @@ export const dynamicPluginsFeatureDiscoveryServiceFactory =
},
});
/**
* A function that returns a backend feature loader that uses the dynamic plugins system to discover features.
*
* @public
*
* @example
* Using the `dynamicPluginsFeatureDiscoveryLoaderWithOptions` loader in a backend instance:
* ```ts
* //...
* import { createBackend } from '@backstage/backend-defaults';
* import { dynamicPluginsFeatureDiscoveryLoaderWithOptions } from '@backstage/backend-dynamic-feature-service';
* import { myCustomModuleLoader } from './myCustomModuleLoader';
*
* const backend = createBackend();
* backend.add(dynamicPluginsFeatureDiscoveryLoaderWithOptions({
* moduleLoader: myCustomModuleLoader
* }));
* //...
* backend.start();
* ```
*/
export const dynamicPluginsFeatureDiscoveryLoaderWithOptions = (
options?: DynamicPluginsFactoryOptions,
) =>
createBackendFeatureLoader({
deps: {
config: coreServices.rootConfig,
logger: coreServices.rootLogger,
},
async loader({ config, logger }) {
const manager = await DynamicPluginManager.create({
config,
logger,
preferAlpha: true,
moduleLoader: options?.moduleLoader?.(logger),
});
const service = new DynamicPluginsEnabledFeatureDiscoveryService(manager);
const { features } = await service.getBackendFeatures();
return features;
},
});
/**
* A backend feature loader that uses the dynamic plugins system to discover features.
*
* @public
*
* @example
* Using the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance:
* ```ts
* //...
* import { createBackend } from '@backstage/backend-defaults';
* import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service';
*
* const backend = createBackend();
* backend.add(dynamicPluginsFeatureDiscoveryLoader);
* //...
* backend.start();
* ```
*/
export const dynamicPluginsFeatureDiscoveryLoader =
dynamicPluginsFeatureDiscoveryLoaderWithOptions();
function isBackendFeature(value: unknown): value is BackendFeature {
return (
!!value &&
@@ -14,7 +14,7 @@ export interface FeatureDiscoveryService {
}>;
}
// @alpha
// @alpha @deprecated
export const featureDiscoveryServiceRef: ServiceRef<
FeatureDiscoveryService,
'root',
+1
View File
@@ -27,6 +27,7 @@ export interface FeatureDiscoveryService {
/**
* An optional service that can be used to dynamically load in additional BackendFeatures at runtime.
* @alpha
* @deprecated The `featureDiscoveryServiceRef` is deprecated in favor of using {@link @backstage/backend-app-api#featureDiscoveryLoader} instead.
*/
export const featureDiscoveryServiceRef =
createServiceRef<FeatureDiscoveryService>({