From 123991d28f49b97e13e8b34a5b679bb056b67269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Mon, 20 Jun 2022 11:22:41 +0100 Subject: [PATCH] refactor: add OpenAPI schema to health-check controller (#1732) * refactor: add OpenAPI schema to health-check controller * refactor: address PR comments * add type to health-check-schema * fix: update snap --- src/lib/openapi/index.ts | 14 +++--- src/lib/openapi/spec/health-check-schema.ts | 17 +++++++ src/lib/routes/health-check.ts | 43 +++++++++++++---- src/lib/routes/index.ts | 2 +- src/lib/services/openapi-service.ts | 5 +- .../__snapshots__/openapi.e2e.test.ts.snap | 46 +++++++++++++++++++ 6 files changed, 110 insertions(+), 17 deletions(-) create mode 100644 src/lib/openapi/spec/health-check-schema.ts diff --git a/src/lib/openapi/index.ts b/src/lib/openapi/index.ts index e07328d0f3..8b9890aa00 100644 --- a/src/lib/openapi/index.ts +++ b/src/lib/openapi/index.ts @@ -18,6 +18,7 @@ import { featureTypesSchema } from './spec/feature-types-schema'; import { featureVariantsSchema } from './spec/feature-variants-schema'; import { featuresSchema } from './spec/features-schema'; import { feedbackSchema } from './spec/feedback-schema'; +import { healthCheckSchema } from './spec/health-check-schema'; import { healthOverviewSchema } from './spec/health-overview-schema'; import { healthReportSchema } from './spec/health-report-schema'; import { legalValueSchema } from './spec/legal-value-schema'; @@ -70,6 +71,7 @@ export const schemas = { featureVariantsSchema, featuresSchema, feedbackSchema, + healthCheckSchema, healthOverviewSchema, healthReportSchema, legalValueSchema, @@ -112,17 +114,15 @@ export interface JsonSchemaProps { components: object; } -export interface AdminApiOperation +interface ApiOperation extends Omit { operationId: string; - tags: ['admin']; + tags: [Tag]; } -export interface ClientApiOperation - extends Omit { - operationId: string; - tags: ['client']; -} +export type AdminApiOperation = ApiOperation<'admin'>; +export type ClientApiOperation = ApiOperation<'client'>; +export type OtherApiOperation = ApiOperation<'other'>; export const createRequestSchema = ( schemaName: string, diff --git a/src/lib/openapi/spec/health-check-schema.ts b/src/lib/openapi/spec/health-check-schema.ts new file mode 100644 index 0000000000..19f1272338 --- /dev/null +++ b/src/lib/openapi/spec/health-check-schema.ts @@ -0,0 +1,17 @@ +import { FromSchema } from 'json-schema-to-ts'; + +export const healthCheckSchema = { + $id: '#/components/schemas/healthCheckSchema', + type: 'object', + additionalProperties: false, + required: ['health'], + properties: { + health: { + type: 'string', + enum: ['GOOD', 'BAD'], + }, + }, + components: {}, +} as const; + +export type HealthCheckSchema = FromSchema; diff --git a/src/lib/routes/health-check.ts b/src/lib/routes/health-check.ts index 0d65f470f3..b1afa6ce11 100644 --- a/src/lib/routes/health-check.ts +++ b/src/lib/routes/health-check.ts @@ -3,33 +3,60 @@ import { IUnleashConfig } from '../types/option'; import { IUnleashServices } from '../types/services'; import { Logger } from '../logger'; import HealthService from '../services/health-service'; +import { OpenApiService } from '../services/openapi-service'; -const Controller = require('./controller'); +import Controller from './controller'; +import { NONE } from '../types/permissions'; +import { createResponseSchema } from '../openapi'; +import { HealthCheckSchema } from '../openapi/spec/health-check-schema'; -class HealthCheckController extends Controller { +export class HealthCheckController extends Controller { private logger: Logger; + private openApiService: OpenApiService; + private healthService: HealthService; constructor( config: IUnleashConfig, - { healthService }: Pick, + { + healthService, + openApiService, + }: Pick, ) { super(config); this.logger = config.getLogger('health-check.js'); + this.openApiService = openApiService; this.healthService = healthService; - this.get('/', (req, res) => this.index(req, res)); + + this.route({ + method: 'get', + path: '', + handler: this.getHealth, + permission: NONE, + middleware: [ + openApiService.validPath({ + tags: ['other'], + operationId: 'getHealth', + responses: { + 200: createResponseSchema('healthCheckSchema'), + 500: createResponseSchema('healthCheckSchema'), + }, + }), + ], + }); } - async index(req: Request, res: Response): Promise { + async getHealth( + _: Request, + res: Response, + ): Promise { try { await this.healthService.dbIsUp(); - res.json({ health: 'GOOD' }); + res.status(200).json({ health: 'GOOD' }); } catch (e) { this.logger.error('Could not select from features, error was: ', e); res.status(500).json({ health: 'BAD' }); } } } -export default HealthCheckController; -module.exports = HealthCheckController; diff --git a/src/lib/routes/index.ts b/src/lib/routes/index.ts index fd8cd4d8c4..471b3b8d4d 100644 --- a/src/lib/routes/index.ts +++ b/src/lib/routes/index.ts @@ -10,7 +10,7 @@ import LogoutController from './logout'; const AdminApi = require('./admin-api'); const ClientApi = require('./client-api'); const Controller = require('./controller'); -const HealthCheckController = require('./health-check'); +import { HealthCheckController } from './health-check'; class IndexRouter extends Controller { constructor(config: IUnleashConfig, services: IUnleashServices) { super(config); diff --git a/src/lib/services/openapi-service.ts b/src/lib/services/openapi-service.ts index 56d1011343..e872ce8e0d 100644 --- a/src/lib/services/openapi-service.ts +++ b/src/lib/services/openapi-service.ts @@ -4,6 +4,7 @@ import { IUnleashConfig } from '../types/option'; import { AdminApiOperation, ClientApiOperation, + OtherApiOperation, createOpenApiSchema, JsonSchemaProps, removeJsonSchemaProps, @@ -30,7 +31,9 @@ export class OpenApiService { ); } - validPath(op: AdminApiOperation | ClientApiOperation): RequestHandler { + validPath( + op: AdminApiOperation | ClientApiOperation | OtherApiOperation, + ): RequestHandler { return this.api.validPath(op); } diff --git a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap index 2e3310ccf1..b405adb82d 100644 --- a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap +++ b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap @@ -580,6 +580,22 @@ Object { "required": Array [], "type": "object", }, + "healthCheckSchema": Object { + "additionalProperties": false, + "properties": Object { + "health": Object { + "enum": Array [ + "GOOD", + "BAD", + ], + "type": "string", + }, + }, + "required": Array [ + "health", + ], + "type": "object", + }, "healthOverviewSchema": Object { "additionalProperties": false, "properties": Object { @@ -3212,6 +3228,36 @@ Object { ], }, }, + "/health": Object { + "get": Object { + "operationId": "getHealth", + "responses": Object { + "200": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "$ref": "#/components/schemas/healthCheckSchema", + }, + }, + }, + "description": "healthCheckSchema", + }, + "500": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "$ref": "#/components/schemas/healthCheckSchema", + }, + }, + }, + "description": "healthCheckSchema", + }, + }, + "tags": Array [ + "other", + ], + }, + }, }, "security": Array [ Object {