backend-defaults,test-utils: use, export, and test health service
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-defaults': patch
|
||||
---
|
||||
|
||||
The `createHealthRouter` utility that allows you to create a health check router is now exported via `@backstage/backend-defaults/rootHttpRouter`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
---
|
||||
|
||||
The default services for `startTestBackend` and `ServiceFactoryTester` now includes the Root Health Service.
|
||||
@@ -3,6 +3,7 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="express" />
|
||||
/// <reference types="node" />
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -17,10 +18,17 @@ import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { RequestHandler } from 'express';
|
||||
import { RequestListener } from 'http';
|
||||
import { RootConfigService } from '@backstage/backend-plugin-api';
|
||||
import { RootHealthService } from '@backstage/backend-plugin-api';
|
||||
import { RootHttpRouterService } from '@backstage/backend-plugin-api';
|
||||
import { Router } from 'express';
|
||||
import type { Server } from 'node:http';
|
||||
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @public (undocumented)
|
||||
export function createHealthRouter(options: {
|
||||
health: RootHealthService;
|
||||
}): Router;
|
||||
|
||||
// @public
|
||||
export function createHttpServer(
|
||||
listener: RequestListener,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { RootHealthService } from '@backstage/backend-plugin-api';
|
||||
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
@@ -16,9 +14,13 @@ import { RootHealthService } from '@backstage/backend-plugin-api';
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { RootHealthService } from '@backstage/backend-plugin-api';
|
||||
import Router from 'express-promise-router';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function createHealthRouter(options: { health: RootHealthService }) {
|
||||
const router = Router();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export {
|
||||
DefaultRootHttpRouter,
|
||||
type DefaultRootHttpRouterOptions,
|
||||
} from './DefaultRootHttpRouter';
|
||||
export { createHealthRouter } from './createHealthRouter';
|
||||
export * from './http';
|
||||
export {
|
||||
rootHttpRouterServiceFactory,
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2022 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 {
|
||||
ServiceFactoryTester,
|
||||
mockServices,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { Express } from 'express';
|
||||
import request from 'supertest';
|
||||
import { rootHttpRouterServiceFactory } from './rootHttpRouterServiceFactory';
|
||||
import { coreServices } from '@backstage/backend-plugin-api';
|
||||
|
||||
describe('rootHttpRouterServiceFactory', () => {
|
||||
it('should make the health endpoints available', async () => {
|
||||
let app: Express | undefined = undefined;
|
||||
|
||||
const tester = ServiceFactoryTester.from(
|
||||
rootHttpRouterServiceFactory({
|
||||
configure(options) {
|
||||
options.applyDefaults();
|
||||
app = options.app;
|
||||
},
|
||||
}),
|
||||
{
|
||||
dependencies: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
app: { baseUrl: 'http://localhost' },
|
||||
backend: {
|
||||
baseUrl: 'http://localhost',
|
||||
listen: { host: '', port: 0 },
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
// Trigger creation of the http service, accessing the app instance through the configure callback
|
||||
await tester.getSubject();
|
||||
|
||||
await request(app!)
|
||||
.get('/.backstage/health/v1/liveness')
|
||||
.expect(200, { status: 'ok' });
|
||||
|
||||
await request(app!).get('/.backstage/health/v1/readiness').expect(503, {
|
||||
message: 'Backend has not started yet',
|
||||
status: 'error',
|
||||
});
|
||||
|
||||
const lifecycle = await tester.getService(coreServices.rootLifecycle);
|
||||
|
||||
await (lifecycle as any).startup(); // Trigger startup by calling the private startup method
|
||||
|
||||
await request(app!).get('/.backstage/health/v1/readiness').expect(200, {
|
||||
status: 'ok',
|
||||
});
|
||||
|
||||
expect('test').toBe('test');
|
||||
});
|
||||
});
|
||||
@@ -242,6 +242,15 @@ describe('TestBackend', () => {
|
||||
expect(res.body).toEqual({ message: 'pong' });
|
||||
});
|
||||
|
||||
it('should expose health check endpoints', async () => {
|
||||
const { server } = await startTestBackend({ features: [] });
|
||||
|
||||
const res = await request(server).get('/.backstage/health/v1/liveness');
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body).toEqual({ status: 'ok' });
|
||||
});
|
||||
|
||||
it('should provide extension point implementations', async () => {
|
||||
expect.assertions(3);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import express from 'express';
|
||||
// Direct internal import to avoid duplication
|
||||
// eslint-disable-next-line @backstage/no-forbidden-package-imports
|
||||
import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring/types';
|
||||
import { createHealthRouter } from '@backstage/backend-defaults/rootHttpRouter';
|
||||
|
||||
/** @public */
|
||||
export interface TestBackendOptions<TExtensionPoints extends any[]> {
|
||||
@@ -73,6 +74,7 @@ export const defaultServiceFactories = [
|
||||
mockServices.lifecycle.factory(),
|
||||
mockServices.logger.factory(),
|
||||
mockServices.permissions.factory(),
|
||||
mockServices.rootHealth.factory(),
|
||||
mockServices.rootLifecycle.factory(),
|
||||
mockServices.rootLogger.factory(),
|
||||
mockServices.scheduler.factory(),
|
||||
@@ -251,15 +253,18 @@ export async function startTestBackend<TExtensionPoints extends any[]>(
|
||||
config: coreServices.rootConfig,
|
||||
lifecycle: coreServices.rootLifecycle,
|
||||
rootLogger: coreServices.rootLogger,
|
||||
health: coreServices.rootHealth,
|
||||
},
|
||||
async factory({ config, lifecycle, rootLogger }) {
|
||||
async factory({ config, lifecycle, rootLogger, health }) {
|
||||
const router = DefaultRootHttpRouter.create();
|
||||
const logger = rootLogger.child({ service: 'rootHttpRouter' });
|
||||
|
||||
const app = express();
|
||||
|
||||
const middleware = MiddlewareFactory.create({ config, logger });
|
||||
const healthRouter = createHealthRouter({ health });
|
||||
|
||||
app.use(healthRouter);
|
||||
app.use(router.handler());
|
||||
app.use(middleware.notFound());
|
||||
app.use(middleware.error());
|
||||
|
||||
Reference in New Issue
Block a user