Merge pull request #26203 from backstage/nbs10/convert-discovery-service-into-loader
[NBS 1.0] Convert discovery services into feature loaders
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Deprecate the `featureDiscoveryServiceFactory` in favor of using `@backstage/backend-defaults#discoveryFeatureLoader` instead.
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
'@backstage/backend-dynamic-feature-service': patch
|
||||
---
|
||||
|
||||
Deprecate the `dynamicPluginsServiceRef`, `dynamicPluginsServiceFactory` and `dynamicPluginsServiceFactoryWithOptions` in favor of using the `dynamicPluginsFeatureDiscoveryLoader` to discover dynamic features in a new backend system.
|
||||
|
||||
See usage examples below:
|
||||
|
||||
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();
|
||||
```
|
||||
|
||||
Passing options to the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance:
|
||||
|
||||
```ts
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service';
|
||||
import { myCustomModuleLoader } from './myCustomModuleLoader';
|
||||
//...
|
||||
|
||||
const backend = createBackend();
|
||||
backend.add(
|
||||
dynamicPluginsFeatureDiscoveryLoader({
|
||||
moduleLoader: myCustomModuleLoader,
|
||||
}),
|
||||
);
|
||||
//...
|
||||
backend.start();
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-plugin-api': patch
|
||||
---
|
||||
|
||||
Deprecate the `featureDiscoveryServiceRef` in favor of using the new `discoveryFeatureLoader` instead.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
'@backstage/backend-defaults': patch
|
||||
---
|
||||
|
||||
Exports the `discoveryFeatureLoader` as a replacement for the deprecated `featureDiscoveryService`.
|
||||
The `discoveryFeatureLoader` is a new backend system [feature loader](https://backstage.io/docs/backend-system/architecture/feature-loaders/) that discovers backend features from the current `package.json` and its dependencies.
|
||||
Here is an example using the `discoveryFeatureLoader` loader in a new backend instance:
|
||||
|
||||
```ts
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
import { discoveryFeatureLoader } from '@backstage/backend-defaults';
|
||||
//...
|
||||
|
||||
const backend = createBackend();
|
||||
//...
|
||||
backend.add(discoveryFeatureLoader);
|
||||
//...
|
||||
backend.start();
|
||||
```
|
||||
@@ -6,7 +6,7 @@
|
||||
import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';
|
||||
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export const featureDiscoveryServiceFactory: ServiceFactory<
|
||||
FeatureDiscoveryService,
|
||||
'root',
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/cli-common": "workspace:^",
|
||||
"@backstage/cli-node": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/config-loader": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
@@ -66,7 +65,6 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"helmet": "^6.0.0",
|
||||
"jose": "^5.0.0",
|
||||
"knex": "^3.0.0",
|
||||
@@ -91,7 +89,6 @@
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/fs-extra": "^11.0.0",
|
||||
"@types/http-errors": "^2.0.0",
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/morgan": "^1.9.0",
|
||||
|
||||
@@ -15,145 +15,17 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
BackendFeature,
|
||||
RootConfigService,
|
||||
RootLoggerService,
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
featureDiscoveryServiceRef,
|
||||
FeatureDiscoveryService,
|
||||
} from '@backstage/backend-plugin-api/alpha';
|
||||
import { resolve as resolvePath, dirname } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { BackstagePackageJson } from '@backstage/cli-node';
|
||||
import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { PackageDiscoveryService } from '../../../backend-defaults/src/PackageDiscoveryService';
|
||||
|
||||
const DETECTED_PACKAGE_ROLES = [
|
||||
'node-library',
|
||||
'backend',
|
||||
'backend-plugin',
|
||||
'backend-plugin-module',
|
||||
];
|
||||
|
||||
/** @internal */
|
||||
async function findClosestPackageDir(
|
||||
searchDir: string,
|
||||
): Promise<string | undefined> {
|
||||
let path = searchDir;
|
||||
|
||||
// Some confidence check to avoid infinite loop
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const packagePath = resolvePath(path, 'package.json');
|
||||
const exists = await fs.pathExists(packagePath);
|
||||
if (exists) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const newPath = dirname(path);
|
||||
if (newPath === path) {
|
||||
return undefined;
|
||||
}
|
||||
path = newPath;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Iteration limit reached when searching for root package.json at ${searchDir}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
class PackageDiscoveryService implements FeatureDiscoveryService {
|
||||
constructor(
|
||||
private readonly config: RootConfigService,
|
||||
private readonly logger: RootLoggerService,
|
||||
) {}
|
||||
|
||||
getDependencyNames(path: string) {
|
||||
const { dependencies } = require(path) as BackstagePackageJson;
|
||||
const packagesConfig = this.config.getOptional('backend.packages');
|
||||
|
||||
const dependencyNames = Object.keys(dependencies || {});
|
||||
|
||||
if (packagesConfig === 'all') {
|
||||
return dependencyNames;
|
||||
}
|
||||
|
||||
const includedPackagesConfig = this.config.getOptionalStringArray(
|
||||
'backend.packages.include',
|
||||
);
|
||||
|
||||
const includedPackages = includedPackagesConfig
|
||||
? new Set(includedPackagesConfig)
|
||||
: dependencyNames;
|
||||
const excludedPackagesSet = new Set(
|
||||
this.config.getOptionalStringArray('backend.packages.exclude'),
|
||||
);
|
||||
|
||||
return [...includedPackages].filter(name => !excludedPackagesSet.has(name));
|
||||
}
|
||||
|
||||
async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {
|
||||
const packagesConfig = this.config.getOptional('backend.packages');
|
||||
if (!packagesConfig || Object.keys(packagesConfig).length === 0) {
|
||||
return { features: [] };
|
||||
}
|
||||
|
||||
const packageDir = await findClosestPackageDir(process.argv[1]);
|
||||
if (!packageDir) {
|
||||
throw new Error('Package discovery failed to find package.json');
|
||||
}
|
||||
const dependencyNames = this.getDependencyNames(
|
||||
resolvePath(packageDir, 'package.json'),
|
||||
);
|
||||
|
||||
const features: BackendFeature[] = [];
|
||||
|
||||
for (const name of dependencyNames) {
|
||||
const depPkg = require(require.resolve(`${name}/package.json`, {
|
||||
paths: [packageDir],
|
||||
})) as BackstagePackageJson;
|
||||
if (
|
||||
!depPkg?.backstage?.role ||
|
||||
!DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)
|
||||
) {
|
||||
continue; // Not a backstage backend package, ignore
|
||||
}
|
||||
|
||||
const exportedModulePaths = [
|
||||
require.resolve(name, {
|
||||
paths: [packageDir],
|
||||
}),
|
||||
];
|
||||
|
||||
// Find modules exported as alpha
|
||||
try {
|
||||
exportedModulePaths.push(
|
||||
require.resolve(`${name}/alpha`, { paths: [packageDir] }),
|
||||
);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
for (const modulePath of exportedModulePaths) {
|
||||
const mod = require(modulePath);
|
||||
|
||||
if (isBackendFeature(mod.default)) {
|
||||
this.logger.info(`Detected: ${name}`);
|
||||
features.push(mod.default);
|
||||
}
|
||||
if (isBackendFeatureFactory(mod.default)) {
|
||||
this.logger.info(`Detected: ${name}`);
|
||||
features.push(mod.default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { features };
|
||||
}
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated The `featureDiscoveryServiceFactory` is deprecated in favor of using {@link @backstage/backend-defaults#discoveryFeatureLoader} instead.
|
||||
*/
|
||||
export const featureDiscoveryServiceFactory = createServiceFactory({
|
||||
service: featureDiscoveryServiceRef,
|
||||
deps: {
|
||||
@@ -164,21 +36,3 @@ export const featureDiscoveryServiceFactory = createServiceFactory({
|
||||
return new PackageDiscoveryService(config, logger);
|
||||
},
|
||||
});
|
||||
|
||||
function isBackendFeature(value: unknown): value is BackendFeature {
|
||||
return (
|
||||
!!value &&
|
||||
['object', 'function'].includes(typeof value) &&
|
||||
(value as BackendFeature).$$type === '@backstage/BackendFeature'
|
||||
);
|
||||
}
|
||||
|
||||
function isBackendFeatureFactory(
|
||||
value: unknown,
|
||||
): value is () => BackendFeature {
|
||||
return (
|
||||
!!value &&
|
||||
typeof value === 'function' &&
|
||||
(value as any).$$type === '@backstage/BackendFeatureFactory'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -157,6 +157,7 @@ export class BackendInitializer {
|
||||
}
|
||||
|
||||
const featureDiscovery = await this.#serviceRegistry.get(
|
||||
// TODO: Let's leave this in place and remove it once the deprecated service is removed. We can do that post-1.0 since it's alpha
|
||||
featureDiscoveryServiceRef,
|
||||
'root',
|
||||
);
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
|
||||
```ts
|
||||
import { Backend } from '@backstage/backend-app-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @public (undocumented)
|
||||
export function createBackend(): Backend;
|
||||
|
||||
// @public
|
||||
export const discoveryFeatureLoader: BackendFeature;
|
||||
```
|
||||
|
||||
@@ -124,6 +124,7 @@
|
||||
"@backstage/backend-dev-utils": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/cli-common": "workspace:^",
|
||||
"@backstage/cli-node": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/config-loader": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2024 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 fs from 'fs-extra';
|
||||
import { resolve as resolvePath, dirname } from 'path';
|
||||
|
||||
import {
|
||||
BackendFeature,
|
||||
RootConfigService,
|
||||
RootLoggerService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';
|
||||
import { BackstagePackageJson } from '@backstage/cli-node';
|
||||
|
||||
const DETECTED_PACKAGE_ROLES = [
|
||||
'node-library',
|
||||
'backend',
|
||||
'backend-plugin',
|
||||
'backend-plugin-module',
|
||||
];
|
||||
|
||||
/** @internal */
|
||||
function isBackendFeature(value: unknown): value is BackendFeature {
|
||||
return (
|
||||
!!value &&
|
||||
['object', 'function'].includes(typeof value) &&
|
||||
(value as BackendFeature).$$type === '@backstage/BackendFeature'
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function isBackendFeatureFactory(
|
||||
value: unknown,
|
||||
): value is () => BackendFeature {
|
||||
return (
|
||||
!!value &&
|
||||
typeof value === 'function' &&
|
||||
(value as any).$$type === '@backstage/BackendFeatureFactory'
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async function findClosestPackageDir(
|
||||
searchDir: string,
|
||||
): Promise<string | undefined> {
|
||||
let path = searchDir;
|
||||
|
||||
// Some confidence check to avoid infinite loop
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const packagePath = resolvePath(path, 'package.json');
|
||||
const exists = await fs.pathExists(packagePath);
|
||||
if (exists) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const newPath = dirname(path);
|
||||
if (newPath === path) {
|
||||
return undefined;
|
||||
}
|
||||
path = newPath;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Iteration limit reached when searching for root package.json at ${searchDir}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export class PackageDiscoveryService implements FeatureDiscoveryService {
|
||||
constructor(
|
||||
private readonly config: RootConfigService,
|
||||
private readonly logger: RootLoggerService,
|
||||
) {}
|
||||
|
||||
getDependencyNames(path: string) {
|
||||
const { dependencies } = require(path) as BackstagePackageJson;
|
||||
const packagesConfig = this.config.getOptional('backend.packages');
|
||||
|
||||
const dependencyNames = Object.keys(dependencies || {});
|
||||
|
||||
if (packagesConfig === 'all') {
|
||||
return dependencyNames;
|
||||
}
|
||||
|
||||
const includedPackagesConfig = this.config.getOptionalStringArray(
|
||||
'backend.packages.include',
|
||||
);
|
||||
|
||||
const includedPackages = includedPackagesConfig
|
||||
? new Set(includedPackagesConfig)
|
||||
: dependencyNames;
|
||||
const excludedPackagesSet = new Set(
|
||||
this.config.getOptionalStringArray('backend.packages.exclude'),
|
||||
);
|
||||
|
||||
return [...includedPackages].filter(name => !excludedPackagesSet.has(name));
|
||||
}
|
||||
|
||||
async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {
|
||||
const packagesConfig = this.config.getOptional('backend.packages');
|
||||
if (!packagesConfig || Object.keys(packagesConfig).length === 0) {
|
||||
return { features: [] };
|
||||
}
|
||||
|
||||
const packageDir = await findClosestPackageDir(process.argv[1]);
|
||||
if (!packageDir) {
|
||||
throw new Error('Package discovery failed to find package.json');
|
||||
}
|
||||
const dependencyNames = this.getDependencyNames(
|
||||
resolvePath(packageDir, 'package.json'),
|
||||
);
|
||||
|
||||
const features: BackendFeature[] = [];
|
||||
|
||||
for (const name of dependencyNames) {
|
||||
const depPkg = require(require.resolve(`${name}/package.json`, {
|
||||
paths: [packageDir],
|
||||
})) as BackstagePackageJson;
|
||||
if (
|
||||
!depPkg?.backstage?.role ||
|
||||
!DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)
|
||||
) {
|
||||
continue; // Not a backstage backend package, ignore
|
||||
}
|
||||
|
||||
const exportedModulePaths = [
|
||||
require.resolve(name, {
|
||||
paths: [packageDir],
|
||||
}),
|
||||
];
|
||||
|
||||
// Find modules exported as alpha
|
||||
try {
|
||||
exportedModulePaths.push(
|
||||
require.resolve(`${name}/alpha`, { paths: [packageDir] }),
|
||||
);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
for (const modulePath of exportedModulePaths) {
|
||||
const mod = require(modulePath);
|
||||
|
||||
if (isBackendFeature(mod.default)) {
|
||||
this.logger.info(`Detected: ${name}`);
|
||||
features.push(mod.default);
|
||||
}
|
||||
if (isBackendFeatureFactory(mod.default)) {
|
||||
this.logger.info(`Detected: ${name}`);
|
||||
features.push(mod.default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { features };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2024 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 {
|
||||
coreServices,
|
||||
createBackendFeatureLoader,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { PackageDiscoveryService } from './PackageDiscoveryService';
|
||||
|
||||
/**
|
||||
* A loader that discovers backend features from the current package.json and its dependencies.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* Using the `discoveryFeatureLoader` loader in a backend instance:
|
||||
* ```ts
|
||||
* //...
|
||||
* import { createBackend } from '@backstage/backend-defaults';
|
||||
* import { discoveryFeatureLoader } from '@backstage/backend-defaults';
|
||||
*
|
||||
* const backend = createBackend();
|
||||
* backend.add(discoveryFeatureLoader);
|
||||
* //...
|
||||
* backend.start();
|
||||
* ```
|
||||
*/
|
||||
export const discoveryFeatureLoader = 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;
|
||||
},
|
||||
});
|
||||
@@ -21,3 +21,4 @@
|
||||
*/
|
||||
|
||||
export { createBackend } from './CreateBackend';
|
||||
export { discoveryFeatureLoader } from './discoveryFeatureLoader';
|
||||
|
||||
@@ -112,7 +112,13 @@ export interface DynamicPluginsFactoryOptions {
|
||||
moduleLoader?(logger: LoggerService): ModuleLoader;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const dynamicPluginsFeatureDiscoveryLoader: ((
|
||||
options?: DynamicPluginsFactoryOptions,
|
||||
) => BackendFeature) &
|
||||
BackendFeature;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const dynamicPluginsFeatureDiscoveryServiceFactory: ServiceFactory<
|
||||
FeatureDiscoveryService,
|
||||
'root',
|
||||
@@ -154,19 +160,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,7 @@ export {
|
||||
dynamicPluginsServiceFactory,
|
||||
dynamicPluginsServiceFactoryWithOptions,
|
||||
dynamicPluginsServiceRef,
|
||||
dynamicPluginsFeatureDiscoveryLoader,
|
||||
} 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} instead.
|
||||
*/
|
||||
export const dynamicPluginsServiceRef = createServiceRef<DynamicPluginProvider>(
|
||||
{
|
||||
@@ -231,6 +233,7 @@ export interface DynamicPluginsFactoryOptions {
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use {@link dynamicPluginsFeatureDiscoveryLoader} 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} instead.
|
||||
*/
|
||||
export const dynamicPluginsFeatureDiscoveryServiceFactory =
|
||||
createServiceFactory({
|
||||
@@ -305,6 +310,66 @@ export const dynamicPluginsFeatureDiscoveryServiceFactory =
|
||||
},
|
||||
});
|
||||
|
||||
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();
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Passing options to the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance:
|
||||
* ```ts
|
||||
* //...
|
||||
* import { createBackend } from '@backstage/backend-defaults';
|
||||
* import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service';
|
||||
* import { myCustomModuleLoader } from './myCustomModuleLoader';
|
||||
*
|
||||
* const backend = createBackend();
|
||||
* backend.add(dynamicPluginsFeatureDiscoveryLoader({
|
||||
* moduleLoader: myCustomModuleLoader
|
||||
* }));
|
||||
* //...
|
||||
* backend.start();
|
||||
* ```
|
||||
*/
|
||||
export const dynamicPluginsFeatureDiscoveryLoader = Object.assign(
|
||||
dynamicPluginsFeatureDiscoveryLoaderWithOptions,
|
||||
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',
|
||||
|
||||
@@ -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-defaults#discoveryFeatureLoader} instead.
|
||||
*/
|
||||
export const featureDiscoveryServiceRef =
|
||||
createServiceRef<FeatureDiscoveryService>({
|
||||
|
||||
@@ -3468,7 +3468,6 @@ __metadata:
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/cli-common": "workspace:^"
|
||||
"@backstage/cli-node": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/config-loader": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
@@ -3477,7 +3476,6 @@ __metadata:
|
||||
"@backstage/types": "workspace:^"
|
||||
"@manypkg/get-packages": ^1.1.3
|
||||
"@types/compression": ^1.7.0
|
||||
"@types/fs-extra": ^11.0.0
|
||||
"@types/http-errors": ^2.0.0
|
||||
"@types/minimist": ^1.2.0
|
||||
"@types/morgan": ^1.9.0
|
||||
@@ -3488,7 +3486,6 @@ __metadata:
|
||||
cors: ^2.8.5
|
||||
express: ^4.17.1
|
||||
express-promise-router: ^4.1.0
|
||||
fs-extra: ^11.2.0
|
||||
helmet: ^6.0.0
|
||||
http-errors: ^2.0.0
|
||||
jose: ^5.0.0
|
||||
@@ -3627,6 +3624,7 @@ __metadata:
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/cli-common": "workspace:^"
|
||||
"@backstage/cli-node": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/config-loader": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user