Refactor backend-test-utils
Co-authored-by: Fredrik Adelöw <freben@users.noreply.github.com> Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com> Signed-off-by: Johan Haals <johan.haals@gmail.com>
This commit is contained in:
@@ -19,15 +19,17 @@ import {
|
||||
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;
|
||||
#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) {
|
||||
@@ -42,7 +44,7 @@ export class BackendInitializer {
|
||||
await Promise.all(
|
||||
Object.entries(deps).map(async ([name, ref]) => [
|
||||
name,
|
||||
this.#extensionPoints.get(ref) ||
|
||||
this.#extensionPoints.get(ref as ExtensionPoint<unknown>) ||
|
||||
(await this.#serviceHolder.get(ref as ServiceRef<unknown>)!(
|
||||
pluginId,
|
||||
)),
|
||||
@@ -66,7 +68,7 @@ export class BackendInitializer {
|
||||
this.#started = true;
|
||||
|
||||
for (const [feature] of this.#features) {
|
||||
const provides = new Set<ServiceRef<unknown>>();
|
||||
const provides = new Set<ExtensionPoint<unknown>>();
|
||||
|
||||
let registerInit: BackendRegisterInit | undefined = undefined;
|
||||
|
||||
@@ -129,13 +131,13 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +150,7 @@ export class BackendInitializer {
|
||||
|
||||
registerInitsToOrder = registerInitsToOrder.filter(r => !toRemove.has(r));
|
||||
}
|
||||
|
||||
return orderedRegisterInits;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import {
|
||||
AnyServiceFactory,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
FactoryFunc,
|
||||
ServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
@@ -32,9 +33,9 @@ export interface Backend {
|
||||
|
||||
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>;
|
||||
|
||||
@@ -38,12 +38,14 @@ export type ExtensionPoint<T> = {
|
||||
/** @public */
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@ import {
|
||||
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({
|
||||
const backend = startTestBackend({
|
||||
services: [
|
||||
[serviceRef, { a: 'a' }],
|
||||
[serviceRef, { a: 'a', b: 'b' }],
|
||||
@@ -68,7 +68,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,
|
||||
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> & {
|
||||
features?: BackendFeature[];
|
||||
},
|
||||
): Promise<void> {
|
||||
const { features = [], ...otherOptions } = options;
|
||||
const backend = createTestBackend(otherOptions);
|
||||
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';
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
|
||||
@@ -23,8 +23,9 @@ describe('ScaffolderCatalogModule', () => {
|
||||
it('should register the extension point', async () => {
|
||||
const extensionPoint = { addProcessor: jest.fn() };
|
||||
await startTestBackend({
|
||||
services: [[catalogProcessingExtensionPoint, extensionPoint]],
|
||||
registrables: [scaffolderCatalogModule({})],
|
||||
services: [],
|
||||
extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]],
|
||||
features: [scaffolderCatalogModule({})],
|
||||
});
|
||||
|
||||
expect(extensionPoint.addProcessor).toHaveBeenCalledWith(
|
||||
|
||||
Reference in New Issue
Block a user