From 918b397763b17f70bab8494bd7b87ddeb084d1b2 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 5 Jun 2024 13:27:10 +0200 Subject: [PATCH] backend-defaults: move over health service Signed-off-by: Vincenzo Scamporlino --- .../health/createHealthRouter.ts | 46 -------------- .../health/healthServiceFactory.ts | 37 ----------- .../backend-defaults/api-report-health.md | 13 ++++ packages/backend-defaults/package.json | 4 ++ .../health/healthServiceFactory.test.ts} | 53 ++++++++-------- .../health/healthServiceFactory.ts | 63 +++++++++++++++++++ .../src/entrypoints}/health/index.ts | 0 7 files changed, 107 insertions(+), 109 deletions(-) delete mode 100644 packages/backend-app-api/src/services/implementations/health/createHealthRouter.ts delete mode 100644 packages/backend-app-api/src/services/implementations/health/healthServiceFactory.ts create mode 100644 packages/backend-defaults/api-report-health.md rename packages/{backend-app-api/src/services/implementations/health/createHealthRouter.test.ts => backend-defaults/src/entrypoints/health/healthServiceFactory.test.ts} (63%) create mode 100644 packages/backend-defaults/src/entrypoints/health/healthServiceFactory.ts rename packages/{backend-app-api/src/services/implementations => backend-defaults/src/entrypoints}/health/index.ts (100%) diff --git a/packages/backend-app-api/src/services/implementations/health/createHealthRouter.ts b/packages/backend-app-api/src/services/implementations/health/createHealthRouter.ts deleted file mode 100644 index f2ff2ecd73..0000000000 --- a/packages/backend-app-api/src/services/implementations/health/createHealthRouter.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 Router from 'express-promise-router'; -import { Request, Response } from 'express'; -import { RootLifecycleService } from '@backstage/backend-plugin-api'; - -export function createHealthRouter(options: { - lifecycle: RootLifecycleService; -}) { - const router = Router(); - - let isRunning = false; - options.lifecycle.addStartupHook(() => { - isRunning = true; - }); - options.lifecycle.addShutdownHook(() => { - isRunning = false; - }); - - router.get('/v1/readiness', async (_request: Request, response: Response) => { - if (!isRunning) { - throw new Error('Backend has not started yet'); - } - response.json({ status: 'ok' }); - }); - - router.get('/v1/liveness', async (_request: Request, response: Response) => { - response.json({ status: 'ok' }); - }); - - return router; -} diff --git a/packages/backend-app-api/src/services/implementations/health/healthServiceFactory.ts b/packages/backend-app-api/src/services/implementations/health/healthServiceFactory.ts deleted file mode 100644 index b79ba34a87..0000000000 --- a/packages/backend-app-api/src/services/implementations/health/healthServiceFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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, - createServiceFactory, -} from '@backstage/backend-plugin-api'; -import { createHealthRouter } from './createHealthRouter'; - -/** - * @public - */ -export const healthServiceFactory = createServiceFactory({ - service: coreServices.health, - deps: { - rootHttpRouter: coreServices.rootHttpRouter, - lifecycle: coreServices.rootLifecycle, - }, - async factory({ lifecycle, rootHttpRouter }) { - rootHttpRouter.use('.backstage/health', createHealthRouter({ lifecycle })); - - return {}; - }, -}); diff --git a/packages/backend-defaults/api-report-health.md b/packages/backend-defaults/api-report-health.md new file mode 100644 index 0000000000..795bdaac78 --- /dev/null +++ b/packages/backend-defaults/api-report-health.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/backend-defaults" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { HealthService } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; + +// @public (undocumented) +export const healthServiceFactory: () => ServiceFactory; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 4c34c7f317..c29d973997 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -24,6 +24,7 @@ "./cache": "./src/entrypoints/cache/index.ts", "./database": "./src/entrypoints/database/index.ts", "./discovery": "./src/entrypoints/discovery/index.ts", + "./health": "./src/entrypoints/health/index.ts", "./httpAuth": "./src/entrypoints/httpAuth/index.ts", "./httpRouter": "./src/entrypoints/httpRouter/index.ts", "./lifecycle": "./src/entrypoints/lifecycle/index.ts", @@ -54,6 +55,9 @@ "discovery": [ "src/entrypoints/discovery/index.ts" ], + "health": [ + "src/entrypoints/health/index.ts" + ], "httpAuth": [ "src/entrypoints/httpAuth/index.ts" ], diff --git a/packages/backend-app-api/src/services/implementations/health/createHealthRouter.test.ts b/packages/backend-defaults/src/entrypoints/health/healthServiceFactory.test.ts similarity index 63% rename from packages/backend-app-api/src/services/implementations/health/createHealthRouter.test.ts rename to packages/backend-defaults/src/entrypoints/health/healthServiceFactory.test.ts index f8c51ee08d..40a41d74f5 100644 --- a/packages/backend-app-api/src/services/implementations/health/createHealthRouter.test.ts +++ b/packages/backend-defaults/src/entrypoints/health/healthServiceFactory.test.ts @@ -1,3 +1,6 @@ +import { mockServices } from '@backstage/backend-test-utils'; +import { DefaultHealthService } from './healthServiceFactory'; + /* * Copyright 2024 The Backstage Authors * @@ -13,22 +16,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { mockServices } from '@backstage/backend-test-utils'; -import request from 'supertest'; -import { createHealthRouter } from './createHealthRouter'; -import express from 'express'; - -describe('createHealthRouter', () => { +describe('DefaultHealthService', () => { describe('readiness', () => { it(`should return a 500 response if the server hasn't started yet`, async () => { - const hc = createHealthRouter({ + const service = new DefaultHealthService({ lifecycle: mockServices.rootLifecycle.mock(), }); - const app = express().use(hc); - - const response = await request(app).get('/v1/readiness'); - expect(response.status).toBe(500); + await expect(service.getReadiness()).resolves.toEqual({ status: 503 }); }); it('should return 200 if the server has started', async () => { @@ -38,12 +32,16 @@ describe('createHealthRouter', () => { fn => (mockServerStartedFn = fn), ); - const hc = createHealthRouter({ lifecycle }); - const app = express().use(hc); + const service = new DefaultHealthService({ + lifecycle: mockServices.rootLifecycle.mock(), + }); mockServerStartedFn(); - const response = await request(app).get('/v1/readiness').expect(200); - expect(response.body).toEqual({ status: 'ok' }); + + await expect(service.getReadiness()).resolves.toEqual({ + status: 200, + payload: { status: 'ok' }, + }); }); it(`should return a 500 response if the server has stopped`, async () => { @@ -57,25 +55,28 @@ describe('createHealthRouter', () => { fn => (mockServerStoppedFn = fn), ); - const hc = createHealthRouter({ lifecycle }); - const app = express().use(hc); + const service = new DefaultHealthService({ + lifecycle: mockServices.rootLifecycle.mock(), + }); mockServerStartedFn(); mockServerStoppedFn(); - const response = await request(app).get('/v1/readiness'); - expect(response.status).toBe(500); + await expect(service.getReadiness()).resolves.toEqual({ + status: 503, + }); }); }); describe('liveness', () => { it('should return 200 if the server has started', async () => { - const lifecycle = mockServices.rootLifecycle.mock(); + const service = new DefaultHealthService({ + lifecycle: mockServices.rootLifecycle.mock(), + }); - const hc = createHealthRouter({ lifecycle }); - const app = express().use(hc); - - const response = await request(app).get('/v1/liveness').expect(200); - expect(response.body).toEqual({ status: 'ok' }); + await expect(service.getLiveness()).resolves.toEqual({ + status: 200, + payload: { status: 'ok' }, + }); }); }); }); diff --git a/packages/backend-defaults/src/entrypoints/health/healthServiceFactory.ts b/packages/backend-defaults/src/entrypoints/health/healthServiceFactory.ts new file mode 100644 index 0000000000..3f397ce29b --- /dev/null +++ b/packages/backend-defaults/src/entrypoints/health/healthServiceFactory.ts @@ -0,0 +1,63 @@ +import { + HealthService, + RootLifecycleService, + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; + +/* + * 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. + */ + +/** @internal */ +export class DefaultHealthService implements HealthService { + #isRunning = false; + + constructor(readonly options: { lifecycle: RootLifecycleService }) { + options.lifecycle.addStartupHook(() => { + this.#isRunning = true; + }); + options.lifecycle.addShutdownHook(() => { + this.#isRunning = false; + }); + } + + async getLiveness(): Promise<{ status: number; payload?: any }> { + return { status: 200, payload: { status: 'ok' } }; + } + async getReadiness(): Promise<{ status: number; payload?: any }> { + if (!this.#isRunning) { + return { + status: 503, + payload: { message: 'Backend has not started yet', status: 'error' }, + }; + } + + return { status: 200, payload: { status: 'ok' } }; + } +} + +/** + * @public + */ +export const healthServiceFactory = createServiceFactory({ + service: coreServices.health, + deps: { + lifecycle: coreServices.rootLifecycle, + }, + async factory({ lifecycle }) { + return new DefaultHealthService({ lifecycle }); + }, +}); diff --git a/packages/backend-app-api/src/services/implementations/health/index.ts b/packages/backend-defaults/src/entrypoints/health/index.ts similarity index 100% rename from packages/backend-app-api/src/services/implementations/health/index.ts rename to packages/backend-defaults/src/entrypoints/health/index.ts