diff --git a/lib/health-check/health-check-executor.service.ts b/lib/health-check/health-check-executor.service.ts index 6f25c597a..348ba7f0f 100644 --- a/lib/health-check/health-check-executor.service.ts +++ b/lib/health-check/health-check-executor.service.ts @@ -5,6 +5,7 @@ import { } from './health-check-result.interface'; import { type HealthCheckError } from '../health-check/health-check.error'; import { + type InferHealthIndicatorResults, type HealthIndicatorFunction, type HealthIndicatorResult, } from '../health-indicator'; @@ -37,13 +38,15 @@ export class HealthCheckExecutor implements BeforeApplicationShutdown { * @returns the result of given health indicators * @param healthIndicators The health indicators which should get executed */ - async execute( - healthIndicators: HealthIndicatorFunction[], - ): Promise { + async execute( + healthIndicators: TFns, + ) { const { results, errors } = await this.executeHealthIndicators(healthIndicators); - return this.getResult(results, errors); + return this.getResult(results, errors) as HealthCheckResult< + InferHealthIndicatorResults + >; } /** diff --git a/lib/health-check/health-check-result.interface.ts b/lib/health-check/health-check-result.interface.ts index 30b1557d6..d5fc51b60 100644 --- a/lib/health-check/health-check-result.interface.ts +++ b/lib/health-check/health-check-result.interface.ts @@ -9,7 +9,15 @@ export type HealthCheckStatus = 'error' | 'ok' | 'shutting_down'; * The result of a health check * @publicApi */ -export interface HealthCheckResult { +export type HealthCheckResult< + TDetails extends HealthIndicatorResult = HealthIndicatorResult, + TInfo extends Partial | undefined = + | Partial + | undefined, + TError extends Partial | undefined = + | Partial + | undefined, +> = { /** * The overall status of the Health Check */ @@ -18,14 +26,14 @@ export interface HealthCheckResult { * The info object contains information of each health indicator * which is of status "up" */ - info?: HealthIndicatorResult; + info?: TInfo; /** * The error object contains information of each health indicator * which is of status "down" */ - error?: HealthIndicatorResult; + error?: TError; /** * The details object contains information of every health indicator. */ - details: HealthIndicatorResult; -} + details: TDetails; +}; diff --git a/lib/health-check/health-check.service.ts b/lib/health-check/health-check.service.ts index 1a7bc940e..d3e221333 100644 --- a/lib/health-check/health-check.service.ts +++ b/lib/health-check/health-check.service.ts @@ -4,11 +4,11 @@ import { Inject, ConsoleLogger, LoggerService, + InternalServerErrorException, } from '@nestjs/common'; import { ErrorLogger } from './error-logger/error-logger.interface'; import { ERROR_LOGGER } from './error-logger/error-logger.provider'; import { HealthCheckExecutor } from './health-check-executor.service'; -import { type HealthCheckResult } from './health-check-result.interface'; import { type HealthIndicatorFunction } from '../health-indicator'; import { TERMINUS_LOGGER } from './logger/logger.provider'; @@ -43,22 +43,31 @@ export class HealthCheckService { * ``` * @param healthIndicators The health indicators which should be checked */ - async check( - healthIndicators: HealthIndicatorFunction[], - ): Promise { + async check( + healthIndicators: TFns, + ) { const result = await this.healthCheckExecutor.execute(healthIndicators); - if (result.status === 'ok') { - return result; - } - if (result.status === 'error') { - const msg = this.errorLogger.getErrorMessage( - 'Health Check has failed!', - result.details, - ); - this.logger.error(msg); - } + switch (result.status) { + case 'ok': + return result; - throw new ServiceUnavailableException(result); + case 'error': + const msg = this.errorLogger.getErrorMessage( + 'Health Check has failed!', + result.details, + ); + this.logger.error(msg); + throw new ServiceUnavailableException(result); + + case 'shutting_down': + throw new ServiceUnavailableException(result); + + default: + // Ensure that we have exhaustively checked all cases + // eslint-disable-next-line unused-imports/no-unused-vars + const exhaustiveCheck: never = result.status; + throw new InternalServerErrorException(); + } } } diff --git a/lib/health-indicator/health-indicator-result.interface.ts b/lib/health-indicator/health-indicator-result.interface.ts index 7eccc3fb6..ae2d47f3d 100644 --- a/lib/health-indicator/health-indicator-result.interface.ts +++ b/lib/health-indicator/health-indicator-result.interface.ts @@ -1,3 +1,5 @@ +import { type HealthIndicatorFunction } from './health-indicator'; + /** * @publicApi */ @@ -12,3 +14,24 @@ export type HealthIndicatorResult< Status extends HealthIndicatorStatus = HealthIndicatorStatus, OptionalData extends Record = Record, > = Record; + +/** + * @internal + */ +export type InferHealthIndicatorResult< + Fn extends HealthIndicatorFunction = HealthIndicatorFunction, +> = Awaited>; + +/** + * @internal + */ +export type InferHealthIndicatorResults< + Fns extends readonly HealthIndicatorFunction[] = HealthIndicatorFunction[], + R extends readonly any[] = [], +> = Fns extends readonly [ + infer Fn extends HealthIndicatorFunction, + ...infer Rest extends readonly HealthIndicatorFunction[], +] + ? InferHealthIndicatorResults & + InferHealthIndicatorResult + : HealthIndicatorResult; diff --git a/lib/health-indicator/health-indicator.service.ts b/lib/health-indicator/health-indicator.service.ts index 97cc98d91..ac4669287 100644 --- a/lib/health-indicator/health-indicator.service.ts +++ b/lib/health-indicator/health-indicator.service.ts @@ -28,13 +28,13 @@ export class HealthIndicatorSession = string> { */ down( data?: T, - ): HealthIndicatorResult; + ): HealthIndicatorResult; down( data?: T, - ): HealthIndicatorResult; + ): HealthIndicatorResult; down( data?: T, - ): HealthIndicatorResult { + ): HealthIndicatorResult { let additionalData: AdditionalData = {}; if (typeof data === 'string') {