diff --git a/api-service/src/configs/RoutesConfig.ts b/api-service/src/configs/RoutesConfig.ts index da7632f1..d3b61a78 100644 --- a/api-service/src/configs/RoutesConfig.ts +++ b/api-service/src/configs/RoutesConfig.ts @@ -165,6 +165,11 @@ export const routesConfig = { method: "get", path: "/status" } + }, + health: { + api_id: "api.health", + method: "get", + path: "/health" } } diff --git a/api-service/src/connectors/DbConnector.ts b/api-service/src/connectors/DbConnector.ts index 6da524bf..fde799d3 100644 --- a/api-service/src/connectors/DbConnector.ts +++ b/api-service/src/connectors/DbConnector.ts @@ -45,6 +45,11 @@ export class DbConnector implements IConnector { return await this.pool.destroy() } + async health() { + await this.connect() + await this.pool.select(1) + } + execute(type: keyof typeof this.typeToMethod, property: any) { this.method = this.typeToMethod[ type ] return this.method(property[ "table" ], property[ "fields" ]) diff --git a/api-service/src/helpers/ErrorResponseHandler.ts b/api-service/src/helpers/ErrorResponseHandler.ts index de6b93c9..f680c3e2 100644 --- a/api-service/src/helpers/ErrorResponseHandler.ts +++ b/api-service/src/helpers/ErrorResponseHandler.ts @@ -15,10 +15,10 @@ export class ErrorResponseHandler { "headers": req.headers, "url": req.url, "error": { - "message": error.message, - "stack": error.stack, - "data": error.data, - "code": error.code, + "message": error?.message, + "stack": error?.stack, + "data": error?.data, + "code": error?.code, "error": error, } })); diff --git a/api-service/src/routes/Router.ts b/api-service/src/routes/Router.ts index fb964565..843b3e8f 100644 --- a/api-service/src/routes/Router.ts +++ b/api-service/src/routes/Router.ts @@ -18,9 +18,11 @@ import { WrapperService } from "../services/WrapperService"; import { onRequest } from "../helpers/prometheus/helpers"; import promEntities from "../helpers/prometheus/entities"; import { metricsScrapeHandler } from "../helpers/prometheus"; +import { HealthService } from "../services/HealthService"; export const validationService = new ValidationService(); -export const queryService = new QueryService(new HTTPConnector(`${config.query_api.druid.host}:${config.query_api.druid.port}`)); +const httpDruidConnector = new HTTPConnector(`${config.query_api.druid.host}:${config.query_api.druid.port}`) +export const queryService = new QueryService(httpDruidConnector); export const kafkaConnector = new KafkaConnector() export const dbConnector = new DbConnector(config.db_connector_config); export const datasourceService = new DataSourceService(dbConnector, config.table_names.datasources); @@ -30,6 +32,7 @@ export const ingestorService = new IngestorService(kafkaConnector,); export const exhaustService = new ClientCloudService(config.exhaust_config.cloud_storage_provider, config.exhaust_config.cloud_storage_config); export const wrapperService = new WrapperService(); export const globalCache: any = new Map() +export const healthService = new HealthService(dbConnector, kafkaConnector, httpDruidConnector) export const router = express.Router() dbConnector.init() /** Query API(s) */ @@ -72,3 +75,4 @@ router.post(routesConfig.query_wrapper.native_post.path, ResponseHandler.setApiI router.get(routesConfig.query_wrapper.native_get.path, ResponseHandler.setApiId(routesConfig.query_wrapper.native_get.api_id), onRequest({ entity: promEntities.data_out }), wrapperService.forwardNativeGet) router.delete(routesConfig.query_wrapper.native_delete.path, ResponseHandler.setApiId(routesConfig.query_wrapper.native_delete.api_id), onRequest({ entity: promEntities.data_out }), wrapperService.forwardNativeDel) router.get(routesConfig.query_wrapper.druid_status.path, ResponseHandler.setApiId(routesConfig.query_wrapper.druid_status.api_id), wrapperService.nativeStatus) +router.get(routesConfig.health.path, ResponseHandler.setApiId(routesConfig.health.api_id), healthService.checkHealth.bind(healthService)) diff --git a/api-service/src/services/HealthService.ts b/api-service/src/services/HealthService.ts new file mode 100644 index 00000000..6a79ede9 --- /dev/null +++ b/api-service/src/services/HealthService.ts @@ -0,0 +1,46 @@ +import { AxiosInstance } from "axios"; +import { NextFunction, Request, Response } from "express"; +import _ from "lodash"; +import { ResponseHandler } from "../helpers/ResponseHandler"; +import { ErrorResponseHandler } from "../helpers/ErrorResponseHandler"; +import { IConnector } from "../models/DatasetModels"; +import { DbConnector } from "../connectors/DbConnector"; +import { KafkaConnector } from "../connectors/KafkaConnector"; + + + + +export class HealthService { + private dbConnector: DbConnector; + private kafkaConnector: KafkaConnector; + private httpDruidConnector: AxiosInstance; + private errorHandler: ErrorResponseHandler; + constructor(dbConnector: DbConnector, kafkaConnector: KafkaConnector, httpDruidConnector: IConnector) { + this.errorHandler = new ErrorResponseHandler("HealthService"); + this.httpDruidConnector = httpDruidConnector.connect() + this.dbConnector = dbConnector; + this.kafkaConnector = kafkaConnector; + } + + checkHealth(req: Request, res: Response, next: NextFunction) { + Promise.all([this.checkDruidHealth(), this.checkKafkaHealth(), this.checkPostgresHealth()]) + .then(() => { + ResponseHandler.successResponse(req, res, { status: 200, data: {} }) + }).catch(error => { + this.errorHandler.handleError(req, res, next, error) + }) + } + + private async checkDruidHealth() { + await this.httpDruidConnector.get("/status/health") + } + + private async checkKafkaHealth() { + await this.kafkaConnector.connect() + } + + private async checkPostgresHealth() { + await this.dbConnector.health() + } + +}