backend-app-api,backend-test-api: hook up shutdown hook to stop method

Co-authored-by: Johan Haals <johan.haals@gmail.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-12-02 11:27:12 +01:00
parent b8aa070dc4
commit 07342778df
7 changed files with 87 additions and 10 deletions
+2
View File
@@ -26,6 +26,8 @@ export interface Backend {
add(feature: BackendFeature): void;
// (undocumented)
start(): Promise<void>;
// (undocumented)
stop(): Promise<void>;
}
// @public (undocumented)
@@ -17,8 +17,10 @@
import {
BackendFeature,
ExtensionPoint,
lifecycleServiceRef,
ServiceRef,
} from '@backstage/backend-plugin-api';
import { BackendLifecycleImpl } from '../services/implementations/lifecycleService';
import {
BackendRegisterInit,
ServiceHolder,
@@ -165,4 +167,24 @@ export class BackendInitializer {
return orderedRegisterInits;
}
async stop(): Promise<void> {
if (!this.#started) {
throw new Error('Backend has not started');
}
this.#started = false;
const lifecycleService = await this.#serviceHolder.get(
lifecycleServiceRef,
'root',
);
// TODO(Rugvip): :D
const lifecycle = (lifecycleService as any)?.lifecycle;
if (lifecycle instanceof BackendLifecycleImpl) {
await lifecycle.shutdown();
} else {
throw new Error('Unexpected lifecycle service implementation');
}
}
}
@@ -36,7 +36,7 @@ export class BackstageBackend implements Backend {
await this.#initializer.start();
}
// async stop(): Promise<void> {
// await this.#initializer.stop();
// }
async stop(): Promise<void> {
await this.#initializer.stop();
}
}
@@ -28,6 +28,7 @@ import { BackstageBackend } from './BackstageBackend';
export interface Backend {
add(feature: BackendFeature): void;
start(): Promise<void>;
stop(): Promise<void>;
}
export interface BackendRegisterInit {
+2 -1
View File
@@ -3,6 +3,7 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { Backend } from '@backstage/backend-app-api';
import { BackendFeature } from '@backstage/backend-plugin-api';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { Knex } from 'knex';
@@ -23,7 +24,7 @@ export function setupRequestMockHandlers(worker: {
export function startTestBackend<
TServices extends any[],
TExtensionPoints extends any[],
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<void>;
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<Backend>;
// @alpha (undocumented)
export interface TestBackendOptions<
@@ -19,6 +19,7 @@ import {
createExtensionPoint,
createServiceFactory,
createServiceRef,
lifecycleServiceRef,
} from '@backstage/backend-plugin-api';
import { startTestBackend } from './TestBackend';
@@ -31,7 +32,7 @@ describe('TestBackend', () => {
const extensionPoint3 = createExtensionPoint<Obj>({ id: 'b3' });
const extensionPoint4 = createExtensionPoint<Obj>({ id: 'b4' });
const extensionPoint5 = createExtensionPoint<Obj>({ id: 'b5' });
await startTestBackend({
const backend = await startTestBackend({
services: [
// @ts-expect-error
[extensionPoint1, { a: 'a' }],
@@ -58,6 +59,7 @@ describe('TestBackend', () => {
],
});
expect(1).toBe(1);
await backend.stop();
});
it('should start the test backend', async () => {
@@ -87,11 +89,40 @@ describe('TestBackend', () => {
},
});
await startTestBackend({
const backend = await startTestBackend({
services: [sf],
features: [testModule()],
});
expect(testFn).toHaveBeenCalledWith('winning');
await backend.stop();
});
it('should stop the test backend', async () => {
const shutdownSpy = jest.fn();
const testModule = createBackendModule({
moduleId: 'test.module',
pluginId: 'test',
register(env) {
env.registerInit({
deps: {
lifecycle: lifecycleServiceRef,
},
async init({ lifecycle }) {
lifecycle.addShutdownHook({ fn: shutdownSpy });
},
});
},
});
const backend = await startTestBackend({
services: [],
features: [testModule()],
});
expect(shutdownSpy).not.toHaveBeenCalled();
await backend.stop();
expect(shutdownSpy).toHaveBeenCalled();
});
});
@@ -14,7 +14,13 @@
* limitations under the License.
*/
import { createSpecializedBackend } from '@backstage/backend-app-api';
import {
Backend,
createSpecializedBackend,
lifecycleFactory,
loggerFactory,
rootLoggerFactory,
} from '@backstage/backend-app-api';
import {
ServiceFactory,
ServiceRef,
@@ -47,11 +53,17 @@ export interface TestBackendOptions<
features?: BackendFeature[];
}
const defaultServiceFactories = [
rootLoggerFactory(),
loggerFactory(),
lifecycleFactory(),
];
/** @alpha */
export async function startTestBackend<
TServices extends any[],
TExtensionPoints extends any[],
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<void> {
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<Backend> {
const {
services = [],
extensionPoints = [],
@@ -69,17 +81,23 @@ export async function startTestBackend<
service: ref,
deps: {},
factory: async () => async () => impl,
});
})();
}
return createServiceFactory({
service: ref,
deps: {},
factory: async () => impl,
});
})();
}
return serviceDef as ServiceFactory;
});
for (const factory of defaultServiceFactories) {
if (!factories.some(f => f.service === factory.service)) {
factories.push(factory);
}
}
const backend = createSpecializedBackend({
...otherOptions,
services: factories,
@@ -101,4 +119,6 @@ export async function startTestBackend<
}
await backend.start();
return backend;
}