Merge pull request #13124 from backstage/mob/feature
backend-app-api: Rename types
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
'@backstage/backend-plugin-api': patch
|
||||
'@backstage/backend-test-utils': patch
|
||||
'@backstage/plugin-catalog-node': patch
|
||||
---
|
||||
|
||||
Refactored experimental backend system with new type names.
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
```ts
|
||||
import { AnyServiceFactory } from '@backstage/backend-plugin-api';
|
||||
import { BackendRegistrable } from '@backstage/backend-plugin-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { HttpRouterService } from '@backstage/backend-plugin-api';
|
||||
import { Logger } from '@backstage/backend-plugin-api';
|
||||
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
||||
@@ -15,13 +16,14 @@ import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
||||
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Backend {
|
||||
// (undocumented)
|
||||
add(extension: BackendRegistrable): void;
|
||||
add(feature: BackendFeature): void;
|
||||
// (undocumented)
|
||||
start(): Promise<void>;
|
||||
}
|
||||
@@ -85,6 +87,11 @@ export const schedulerFactory: ServiceFactory<
|
||||
{}
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ServiceOrExtensionPoint<T = unknown> =
|
||||
| ExtensionPoint<T>
|
||||
| ServiceRef<T>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const tokenManagerFactory: ServiceFactory<
|
||||
TokenManager,
|
||||
|
||||
@@ -15,19 +15,21 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
BackendRegistrable,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
ServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { BackendRegisterInit, ServiceHolder } from './types';
|
||||
|
||||
type ServiceOrExtensionPoint = ExtensionPoint<unknown> | ServiceRef<unknown>;
|
||||
import {
|
||||
BackendRegisterInit,
|
||||
ServiceHolder,
|
||||
ServiceOrExtensionPoint,
|
||||
} from './types';
|
||||
|
||||
export class BackendInitializer {
|
||||
#started = false;
|
||||
#extensions = new Map<BackendRegistrable, unknown>();
|
||||
#features = new Map<BackendFeature, unknown>();
|
||||
#registerInits = new Array<BackendRegisterInit>();
|
||||
#extensionPoints = new Map<ServiceOrExtensionPoint, unknown>();
|
||||
#extensionPoints = new Map<ExtensionPoint<unknown>, unknown>();
|
||||
#serviceHolder: ServiceHolder;
|
||||
|
||||
constructor(serviceHolder: ServiceHolder) {
|
||||
@@ -38,42 +40,56 @@ export class BackendInitializer {
|
||||
deps: { [name: string]: ServiceOrExtensionPoint },
|
||||
pluginId: string,
|
||||
) {
|
||||
return Object.fromEntries(
|
||||
await Promise.all(
|
||||
Object.entries(deps).map(async ([name, ref]) => [
|
||||
name,
|
||||
this.#extensionPoints.get(ref) ||
|
||||
(await this.#serviceHolder.get(ref as ServiceRef<unknown>)!(
|
||||
pluginId,
|
||||
)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
const result = new Map<string, unknown>();
|
||||
const missingRefs = new Set<ServiceOrExtensionPoint>();
|
||||
|
||||
add<TOptions>(extension: BackendRegistrable, options?: TOptions) {
|
||||
if (this.#started) {
|
||||
for (const [name, ref] of Object.entries(deps)) {
|
||||
const extensionPoint = this.#extensionPoints.get(
|
||||
ref as ExtensionPoint<unknown>,
|
||||
);
|
||||
if (extensionPoint) {
|
||||
result.set(name, extensionPoint);
|
||||
} else {
|
||||
const factory = await this.#serviceHolder.get(
|
||||
ref as ServiceRef<unknown>,
|
||||
);
|
||||
if (factory) {
|
||||
result.set(name, await factory(pluginId));
|
||||
} else {
|
||||
missingRefs.add(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingRefs.size > 0) {
|
||||
const missing = Array.from(missingRefs).join(', ');
|
||||
throw new Error(
|
||||
'extension can not be added after the backend has started',
|
||||
`No extension point or service available for the following ref(s): ${missing}`,
|
||||
);
|
||||
}
|
||||
this.#extensions.set(extension, options);
|
||||
|
||||
return Object.fromEntries(result);
|
||||
}
|
||||
|
||||
add<TOptions>(feature: BackendFeature, options?: TOptions) {
|
||||
if (this.#started) {
|
||||
throw new Error('feature can not be added after the backend has started');
|
||||
}
|
||||
this.#features.set(feature, options);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
console.log(`Starting backend`);
|
||||
if (this.#started) {
|
||||
throw new Error('Backend has already started');
|
||||
}
|
||||
this.#started = true;
|
||||
|
||||
for (const [extension] of this.#extensions) {
|
||||
const provides = new Set<ServiceRef<unknown>>();
|
||||
for (const [feature] of this.#features) {
|
||||
const provides = new Set<ExtensionPoint<unknown>>();
|
||||
|
||||
let registerInit: BackendRegisterInit | undefined = undefined;
|
||||
|
||||
console.log('Registering', extension.id);
|
||||
extension.register({
|
||||
feature.register({
|
||||
registerExtensionPoint: (extensionPointRef, impl) => {
|
||||
if (registerInit) {
|
||||
throw new Error('registerExtensionPoint called after registerInit');
|
||||
@@ -89,7 +105,7 @@ export class BackendInitializer {
|
||||
throw new Error('registerInit must only be called once');
|
||||
}
|
||||
registerInit = {
|
||||
id: extension.id,
|
||||
id: feature.id,
|
||||
provides,
|
||||
consumes: new Set(Object.values(registerOptions.deps)),
|
||||
deps: registerOptions.deps,
|
||||
@@ -100,15 +116,13 @@ export class BackendInitializer {
|
||||
|
||||
if (!registerInit) {
|
||||
throw new Error(
|
||||
`registerInit was not called by register in ${extension.id}`,
|
||||
`registerInit was not called by register in ${feature.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
this.#registerInits.push(registerInit);
|
||||
}
|
||||
|
||||
this.validateSetup();
|
||||
|
||||
const orderedRegisterResults = this.#resolveInitOrder(this.#registerInits);
|
||||
|
||||
for (const registerInit of orderedRegisterResults) {
|
||||
@@ -117,8 +131,6 @@ export class BackendInitializer {
|
||||
}
|
||||
}
|
||||
|
||||
private validateSetup() {}
|
||||
|
||||
#resolveInitOrder(registerInits: Array<BackendRegisterInit>) {
|
||||
let registerInitsToOrder = registerInits.slice();
|
||||
const orderedRegisterInits = new Array<BackendRegisterInit>();
|
||||
@@ -131,18 +143,17 @@ export class BackendInitializer {
|
||||
for (const registerInit of registerInitsToOrder) {
|
||||
const unInitializedDependents = [];
|
||||
|
||||
for (const serviceRef of registerInit.provides) {
|
||||
for (const provided of registerInit.provides) {
|
||||
if (
|
||||
registerInitsToOrder.some(
|
||||
init => init !== registerInit && init.consumes.has(serviceRef),
|
||||
init => init !== registerInit && init.consumes.has(provided),
|
||||
)
|
||||
) {
|
||||
unInitializedDependents.push(serviceRef);
|
||||
unInitializedDependents.push(provided);
|
||||
}
|
||||
}
|
||||
|
||||
if (unInitializedDependents.length === 0) {
|
||||
console.log(`DEBUG: pushed ${registerInit.id} to results`);
|
||||
orderedRegisterInits.push(registerInit);
|
||||
toRemove.add(registerInit);
|
||||
}
|
||||
@@ -150,6 +161,7 @@ export class BackendInitializer {
|
||||
|
||||
registerInitsToOrder = registerInitsToOrder.filter(r => !toRemove.has(r));
|
||||
}
|
||||
|
||||
return orderedRegisterInits;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import {
|
||||
AnyServiceFactory,
|
||||
BackendRegistrable,
|
||||
BackendFeature,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { BackendInitializer } from './BackendInitializer';
|
||||
import { ServiceRegistry } from './ServiceRegistry';
|
||||
@@ -31,8 +31,8 @@ export class BackstageBackend implements Backend {
|
||||
this.#initializer = new BackendInitializer(this.#services);
|
||||
}
|
||||
|
||||
add(extension: BackendRegistrable): void {
|
||||
this.#initializer.add(extension);
|
||||
add(feature: BackendFeature): void {
|
||||
this.#initializer.add(feature);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
|
||||
@@ -14,5 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type { Backend, CreateSpecializedBackendOptions } from './types';
|
||||
export type {
|
||||
Backend,
|
||||
CreateSpecializedBackendOptions,
|
||||
ServiceOrExtensionPoint,
|
||||
} from './types';
|
||||
export { createSpecializedBackend } from './types';
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
import {
|
||||
AnyServiceFactory,
|
||||
BackendRegistrable,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
FactoryFunc,
|
||||
ServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
@@ -26,15 +27,15 @@ import { BackstageBackend } from './BackstageBackend';
|
||||
* @public
|
||||
*/
|
||||
export interface Backend {
|
||||
add(extension: BackendRegistrable): void;
|
||||
add(feature: BackendFeature): void;
|
||||
start(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface BackendRegisterInit {
|
||||
id: string;
|
||||
consumes: Set<ServiceRef<unknown>>;
|
||||
provides: Set<ServiceRef<unknown>>;
|
||||
deps: { [name: string]: ServiceRef<unknown> };
|
||||
consumes: Set<ServiceOrExtensionPoint>;
|
||||
provides: Set<ServiceOrExtensionPoint>;
|
||||
deps: { [name: string]: ServiceOrExtensionPoint };
|
||||
init: (deps: { [name: string]: unknown }) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -57,3 +58,10 @@ export function createSpecializedBackend(
|
||||
): Backend {
|
||||
return new BackstageBackend(options.services);
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ServiceOrExtensionPoint<T = unknown> =
|
||||
| ExtensionPoint<T>
|
||||
| ServiceRef<T>;
|
||||
|
||||
@@ -26,23 +26,11 @@ export type AnyServiceFactory = ServiceFactory<
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BackendInitRegistry {
|
||||
export interface BackendFeature {
|
||||
// (undocumented)
|
||||
registerExtensionPoint<TExtensionPoint>(
|
||||
ref: ServiceRef<TExtensionPoint>,
|
||||
impl: TExtensionPoint,
|
||||
): void;
|
||||
id: string;
|
||||
// (undocumented)
|
||||
registerInit<
|
||||
Deps extends {
|
||||
[name in string]: unknown;
|
||||
},
|
||||
>(options: {
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]>;
|
||||
};
|
||||
init: (deps: Deps) => Promise<void>;
|
||||
}): void;
|
||||
register(reg: BackendRegistrationPoints): void;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -53,7 +41,7 @@ export interface BackendModuleConfig<TOptions> {
|
||||
pluginId: string;
|
||||
// (undocumented)
|
||||
register(
|
||||
reg: Omit<BackendInitRegistry, 'registerExtensionPoint'>,
|
||||
reg: Omit<BackendRegistrationPoints, 'registerExtensionPoint'>,
|
||||
options: TOptions,
|
||||
): void;
|
||||
}
|
||||
@@ -63,15 +51,27 @@ export interface BackendPluginConfig<TOptions> {
|
||||
// (undocumented)
|
||||
id: string;
|
||||
// (undocumented)
|
||||
register(reg: BackendInitRegistry, options: TOptions): void;
|
||||
register(reg: BackendRegistrationPoints, options: TOptions): void;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BackendRegistrable {
|
||||
export interface BackendRegistrationPoints {
|
||||
// (undocumented)
|
||||
id: string;
|
||||
registerExtensionPoint<TExtensionPoint>(
|
||||
ref: ExtensionPoint<TExtensionPoint>,
|
||||
impl: TExtensionPoint,
|
||||
): void;
|
||||
// (undocumented)
|
||||
register(reg: BackendInitRegistry): void;
|
||||
registerInit<
|
||||
Deps extends {
|
||||
[name in string]: unknown;
|
||||
},
|
||||
>(options: {
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]> | ExtensionPoint<Deps[name]>;
|
||||
};
|
||||
init(deps: Deps): Promise<void>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -84,15 +84,15 @@ export const configServiceRef: ServiceRef<Config>;
|
||||
export function createBackendModule<TOptions>(
|
||||
config: BackendModuleConfig<TOptions>,
|
||||
): undefined extends TOptions
|
||||
? (options?: TOptions) => BackendRegistrable
|
||||
: (options: TOptions) => BackendRegistrable;
|
||||
? (options?: TOptions) => BackendFeature
|
||||
: (options: TOptions) => BackendFeature;
|
||||
|
||||
// @public (undocumented)
|
||||
export function createBackendPlugin<TOptions>(
|
||||
config: BackendPluginConfig<TOptions>,
|
||||
): undefined extends TOptions
|
||||
? (options?: TOptions) => BackendRegistrable
|
||||
: (options: TOptions) => BackendRegistrable;
|
||||
? (options?: TOptions) => BackendFeature
|
||||
: (options: TOptions) => BackendFeature;
|
||||
|
||||
// @public (undocumented)
|
||||
export function createExtensionPoint<T>(options: {
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
BackendInitRegistry,
|
||||
BackendRegistrable,
|
||||
BackendRegistrationPoints,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
} from './types';
|
||||
|
||||
@@ -39,18 +39,18 @@ export function createExtensionPoint<T>(options: {
|
||||
/** @public */
|
||||
export interface BackendPluginConfig<TOptions> {
|
||||
id: string;
|
||||
register(reg: BackendInitRegistry, options: TOptions): void;
|
||||
register(reg: BackendRegistrationPoints, options: TOptions): void;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createBackendPlugin<TOptions>(
|
||||
config: BackendPluginConfig<TOptions>,
|
||||
): undefined extends TOptions
|
||||
? (options?: TOptions) => BackendRegistrable
|
||||
: (options: TOptions) => BackendRegistrable {
|
||||
? (options?: TOptions) => BackendFeature
|
||||
: (options: TOptions) => BackendFeature {
|
||||
return (options?: TOptions) => ({
|
||||
id: config.id,
|
||||
register(register: BackendInitRegistry) {
|
||||
register(register: BackendRegistrationPoints) {
|
||||
return config.register(register, options!);
|
||||
},
|
||||
});
|
||||
@@ -61,7 +61,7 @@ export interface BackendModuleConfig<TOptions> {
|
||||
pluginId: string;
|
||||
moduleId: string;
|
||||
register(
|
||||
reg: Omit<BackendInitRegistry, 'registerExtensionPoint'>,
|
||||
reg: Omit<BackendRegistrationPoints, 'registerExtensionPoint'>,
|
||||
options: TOptions,
|
||||
): void;
|
||||
}
|
||||
@@ -70,11 +70,11 @@ export interface BackendModuleConfig<TOptions> {
|
||||
export function createBackendModule<TOptions>(
|
||||
config: BackendModuleConfig<TOptions>,
|
||||
): undefined extends TOptions
|
||||
? (options?: TOptions) => BackendRegistrable
|
||||
: (options: TOptions) => BackendRegistrable {
|
||||
? (options?: TOptions) => BackendFeature
|
||||
: (options: TOptions) => BackendFeature {
|
||||
return (options?: TOptions) => ({
|
||||
id: `${config.pluginId}.${config.moduleId}`,
|
||||
register(register: BackendInitRegistry) {
|
||||
register(register: BackendRegistrationPoints) {
|
||||
// TODO: Hide registerExtensionPoint
|
||||
return config.register(register, options!);
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ export {
|
||||
createExtensionPoint,
|
||||
} from './factories';
|
||||
export type {
|
||||
BackendInitRegistry,
|
||||
BackendRegistrable,
|
||||
BackendRegistrationPoints,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
} from './types';
|
||||
|
||||
@@ -36,19 +36,22 @@ export type ExtensionPoint<T> = {
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface BackendInitRegistry {
|
||||
export interface BackendRegistrationPoints {
|
||||
registerExtensionPoint<TExtensionPoint>(
|
||||
ref: ServiceRef<TExtensionPoint>,
|
||||
ref: ExtensionPoint<TExtensionPoint>,
|
||||
impl: TExtensionPoint,
|
||||
): void;
|
||||
registerInit<Deps extends { [name in string]: unknown }>(options: {
|
||||
deps: { [name in keyof Deps]: ServiceRef<Deps[name]> };
|
||||
init: (deps: Deps) => Promise<void>;
|
||||
deps: {
|
||||
[name in keyof Deps]: ServiceRef<Deps[name]> | ExtensionPoint<Deps[name]>;
|
||||
};
|
||||
init(deps: Deps): Promise<void>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface BackendRegistrable {
|
||||
export interface BackendFeature {
|
||||
// TODO(Rugvip): Try to get rid of the ID at this level, allowing for a feature to register multiple features as a bundle
|
||||
id: string;
|
||||
register(reg: BackendInitRegistry): void;
|
||||
register(reg: BackendRegistrationPoints): void;
|
||||
}
|
||||
|
||||
@@ -4,16 +4,11 @@
|
||||
|
||||
```ts
|
||||
import { AnyServiceFactory } from '@backstage/backend-plugin-api';
|
||||
import { Backend } from '@backstage/backend-app-api';
|
||||
import { BackendRegistrable } from '@backstage/backend-plugin-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { Knex } from 'knex';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createTestBackend<TServices extends any[]>(
|
||||
options: TestBackendOptions<TServices>,
|
||||
): Backend;
|
||||
|
||||
// @public (undocumented)
|
||||
export function isDockerDisabledForTests(): boolean;
|
||||
|
||||
@@ -25,16 +20,29 @@ export function setupRequestMockHandlers(worker: {
|
||||
}): void;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function startTestBackend<TServices extends any[]>(
|
||||
options: TestBackendOptions<TServices> & {
|
||||
registrables?: BackendRegistrable[];
|
||||
},
|
||||
): Promise<void>;
|
||||
export function startTestBackend<
|
||||
TServices extends any[],
|
||||
TExtensionPoints extends any[],
|
||||
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<void>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface TestBackendOptions<TServices extends any[]> {
|
||||
export interface TestBackendOptions<
|
||||
TServices extends any[],
|
||||
TExtensionPoints extends any[],
|
||||
> {
|
||||
// (undocumented)
|
||||
services: readonly [
|
||||
extensionPoints?: readonly [
|
||||
...{
|
||||
[index in keyof TExtensionPoints]: [
|
||||
ExtensionPoint<TExtensionPoints[index]>,
|
||||
Partial<TExtensionPoints[index]>,
|
||||
];
|
||||
},
|
||||
];
|
||||
// (undocumented)
|
||||
features?: BackendFeature[];
|
||||
// (undocumented)
|
||||
services?: readonly [
|
||||
...{
|
||||
[index in keyof TServices]:
|
||||
| AnyServiceFactory
|
||||
|
||||
@@ -16,16 +16,25 @@
|
||||
|
||||
import {
|
||||
createBackendModule,
|
||||
createExtensionPoint,
|
||||
createServiceFactory,
|
||||
createServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createTestBackend, startTestBackend } from './TestBackend';
|
||||
import { startTestBackend } from './TestBackend';
|
||||
|
||||
describe('TestBackend', () => {
|
||||
it('should get a type error if service implementation does not match', () => {
|
||||
const serviceRef = createServiceRef<{ a: string; b: string }>({ id: 'a' });
|
||||
const backend = createTestBackend({
|
||||
it('should get a type error if service implementation does not match', async () => {
|
||||
type Obj = { a: string; b: string };
|
||||
const serviceRef = createServiceRef<Obj>({ id: 'a' });
|
||||
const extensionPoint1 = createExtensionPoint<Obj>({ id: 'b1' });
|
||||
const extensionPoint2 = createExtensionPoint<Obj>({ id: 'b2' });
|
||||
const extensionPoint3 = createExtensionPoint<Obj>({ id: 'b3' });
|
||||
const extensionPoint4 = createExtensionPoint<Obj>({ id: 'b4' });
|
||||
const extensionPoint5 = createExtensionPoint<Obj>({ id: 'b5' });
|
||||
await startTestBackend({
|
||||
services: [
|
||||
// @ts-expect-error
|
||||
[extensionPoint1, { a: 'a' }],
|
||||
[serviceRef, { a: 'a' }],
|
||||
[serviceRef, { a: 'a', b: 'b' }],
|
||||
// @ts-expect-error
|
||||
@@ -35,8 +44,20 @@ describe('TestBackend', () => {
|
||||
// @ts-expect-error
|
||||
[serviceRef, { a: 'a', b: 'b', c: 'c' }],
|
||||
],
|
||||
extensionPoints: [
|
||||
// @ts-expect-error
|
||||
[serviceRef, { a: 'a' }],
|
||||
[extensionPoint1, { a: 'a' }],
|
||||
[extensionPoint2, { a: 'a', b: 'b' }],
|
||||
// @ts-expect-error
|
||||
[extensionPoint3, { c: 'c' }],
|
||||
// @ts-expect-error
|
||||
[extensionPoint4, { a: 'a', c: 'c' }],
|
||||
// @ts-expect-error
|
||||
[extensionPoint5, { a: 'a', b: 'b', c: 'c' }],
|
||||
],
|
||||
});
|
||||
expect(backend).toBeDefined();
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
|
||||
it('should start the test backend', async () => {
|
||||
@@ -68,7 +89,7 @@ describe('TestBackend', () => {
|
||||
|
||||
await startTestBackend({
|
||||
services: [sf],
|
||||
registrables: [testModule({})],
|
||||
features: [testModule({})],
|
||||
});
|
||||
|
||||
expect(testFn).toBeCalledWith('winning');
|
||||
|
||||
@@ -14,31 +14,54 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Backend, createSpecializedBackend } from '@backstage/backend-app-api';
|
||||
import { createSpecializedBackend } from '@backstage/backend-app-api';
|
||||
import {
|
||||
AnyServiceFactory,
|
||||
ServiceRef,
|
||||
createServiceFactory,
|
||||
BackendRegistrable,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
/** @alpha */
|
||||
export interface TestBackendOptions<TServices extends any[]> {
|
||||
services: readonly [
|
||||
export interface TestBackendOptions<
|
||||
TServices extends any[],
|
||||
TExtensionPoints extends any[],
|
||||
> {
|
||||
services?: readonly [
|
||||
...{
|
||||
[index in keyof TServices]:
|
||||
| AnyServiceFactory
|
||||
| [ServiceRef<TServices[index]>, Partial<TServices[index]>];
|
||||
},
|
||||
];
|
||||
extensionPoints?: readonly [
|
||||
...{
|
||||
[index in keyof TExtensionPoints]: [
|
||||
ExtensionPoint<TExtensionPoints[index]>,
|
||||
Partial<TExtensionPoints[index]>,
|
||||
];
|
||||
},
|
||||
];
|
||||
features?: BackendFeature[];
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export function createTestBackend<TServices extends any[]>(
|
||||
options: TestBackendOptions<TServices>,
|
||||
): Backend {
|
||||
const factories = options.services?.map(serviceDef => {
|
||||
export async function startTestBackend<
|
||||
TServices extends any[],
|
||||
TExtensionPoints extends any[],
|
||||
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<void> {
|
||||
const {
|
||||
services = [],
|
||||
extensionPoints = [],
|
||||
features = [],
|
||||
...otherOptions
|
||||
} = options;
|
||||
|
||||
const factories = services.map(serviceDef => {
|
||||
if (Array.isArray(serviceDef)) {
|
||||
// if type is ExtensionPoint?
|
||||
// do something differently?
|
||||
return createServiceFactory({
|
||||
service: serviceDef[0],
|
||||
deps: {},
|
||||
@@ -47,19 +70,26 @@ export function createTestBackend<TServices extends any[]>(
|
||||
}
|
||||
return serviceDef as AnyServiceFactory;
|
||||
});
|
||||
return createSpecializedBackend({ services: factories ?? [] });
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export async function startTestBackend<TServices extends any[]>(
|
||||
options: TestBackendOptions<TServices> & {
|
||||
registrables?: BackendRegistrable[];
|
||||
},
|
||||
): Promise<void> {
|
||||
const { registrables = [], ...otherOptions } = options;
|
||||
const backend = createTestBackend(otherOptions);
|
||||
for (const reg of registrables) {
|
||||
backend.add(reg);
|
||||
const backend = createSpecializedBackend({
|
||||
...otherOptions,
|
||||
services: factories,
|
||||
});
|
||||
|
||||
backend.add({
|
||||
id: `---test-extension-point-registrar`,
|
||||
register(reg) {
|
||||
for (const [ref, impl] of extensionPoints) {
|
||||
reg.registerExtensionPoint(ref, impl);
|
||||
}
|
||||
|
||||
reg.registerInit({ deps: {}, async init() {} });
|
||||
},
|
||||
});
|
||||
|
||||
for (const feature of features) {
|
||||
backend.add(feature);
|
||||
}
|
||||
|
||||
await backend.start();
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { createTestBackend, startTestBackend } from './TestBackend';
|
||||
export { startTestBackend } from './TestBackend';
|
||||
export type { TestBackendOptions } from './TestBackend';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { BackendRegistrable } from '@backstage/backend-plugin-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
|
||||
import { CatalogProcessor } from '@backstage/plugin-catalog-node';
|
||||
@@ -224,7 +224,7 @@ export type CatalogPermissionRule<TParams extends unknown[] = unknown[]> =
|
||||
PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
|
||||
|
||||
// @alpha
|
||||
export const catalogPlugin: (options?: unknown) => BackendRegistrable;
|
||||
export const catalogPlugin: (options?: unknown) => BackendFeature;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CatalogProcessingEngine {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface CatalogProcessingExtensionPoint {
|
||||
@@ -19,7 +19,7 @@ export interface CatalogProcessingExtensionPoint {
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const catalogProcessingExtensionPoint: ServiceRef<CatalogProcessingExtensionPoint>;
|
||||
export const catalogProcessingExtensionPoint: ExtensionPoint<CatalogProcessingExtensionPoint>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type CatalogProcessor = {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createServiceRef } from '@backstage/backend-plugin-api';
|
||||
import { createExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { EntityProvider } from './api';
|
||||
import { CatalogProcessor } from './api/processor';
|
||||
|
||||
@@ -29,6 +29,6 @@ export interface CatalogProcessingExtensionPoint {
|
||||
* @alpha
|
||||
*/
|
||||
export const catalogProcessingExtensionPoint =
|
||||
createServiceRef<CatalogProcessingExtensionPoint>({
|
||||
createExtensionPoint<CatalogProcessingExtensionPoint>({
|
||||
id: 'catalog.processing',
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { BackendRegistrable } from '@backstage/backend-plugin-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { CatalogProcessor } from '@backstage/plugin-catalog-backend';
|
||||
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend';
|
||||
@@ -566,7 +566,7 @@ export type RunCommandOptions = {
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export const scaffolderCatalogModule: (options?: unknown) => BackendRegistrable;
|
||||
export const scaffolderCatalogModule: (options?: unknown) => BackendFeature;
|
||||
|
||||
// @public (undocumented)
|
||||
export class ScaffolderEntitiesProcessor implements CatalogProcessor {
|
||||
|
||||
@@ -23,8 +23,8 @@ describe('ScaffolderCatalogModule', () => {
|
||||
it('should register the extension point', async () => {
|
||||
const extensionPoint = { addProcessor: jest.fn() };
|
||||
await startTestBackend({
|
||||
services: [[catalogProcessingExtensionPoint, extensionPoint]],
|
||||
registrables: [scaffolderCatalogModule({})],
|
||||
extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]],
|
||||
features: [scaffolderCatalogModule({})],
|
||||
});
|
||||
|
||||
expect(extensionPoint.addProcessor).toHaveBeenCalledWith(
|
||||
|
||||
Reference in New Issue
Block a user