diff --git a/README.md b/README.md index 2aead18..adea676 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,9 @@ All you need to give is just health check function returning cats `ValidationNel val response = Http().singleRequest(HttpRequest(uri = "http://localhost:8888/health")) - // response body would be an json object similar to below. + // status code is 200(OK) if healthy, 500(Internal Server Error) if unhealthy. + // response body is empty by default for performance. + // pass '?full=true' query parameter to see full check result as json. it would be similar to below. // Please see com.github.everpeace.healthchecks.HealthRoutesTest for various response patterns. // { // "status": "healthy", diff --git a/core/src/main/scala/com/github/everpeace/healthchecks/route/HealthCheckRoutes.scala b/core/src/main/scala/com/github/everpeace/healthchecks/route/HealthCheckRoutes.scala index 433b973..341008f 100644 --- a/core/src/main/scala/com/github/everpeace/healthchecks/route/HealthCheckRoutes.scala +++ b/core/src/main/scala/com/github/everpeace/healthchecks/route/HealthCheckRoutes.scala @@ -30,11 +30,12 @@ package com.github.everpeace.healthchecks.route import akka.http.scaladsl.model.StatusCodes._ import akka.http.scaladsl.server.Directives._ -import akka.http.scaladsl.server.{PathMatchers, Route} import akka.http.scaladsl.server.directives.PathDirectives +import akka.http.scaladsl.server.{PathMatchers, Route} import cats.data.Validated.{Invalid, Valid} import com.github.everpeace.healthchecks.{HealthCheck, HealthCheckResult} import de.heikoseeberger.akkahttpcirce.CirceSupport._ +import io.circe.JsonObject import io.circe.generic.JsonCodec import io.circe.generic.auto._ @@ -48,10 +49,13 @@ object HealthCheckRoutes extends DecorateAsScala { severity: String, status: String, messages: List[String]) + @JsonCodec case class ResponseJson(status: String, check_results: List[HealthCheckResultJson]) - private def status(s: Boolean) = if (s) "healthy" else "unhealthy" + private def status(s: Boolean) = if (s) "healthy" else "unhealthy" + private def statusCode(s: Boolean) = if (s) OK else InternalServerError + private def toResultJson(check: HealthCheck, result: HealthCheckResult) = HealthCheckResultJson( check.name, @@ -83,21 +87,32 @@ object HealthCheckRoutes extends DecorateAsScala { val rootSlashRemoved = if (path.startsWith("/")) path.substring(1) else path PathDirectives.path(PathMatchers.separateOnSlashes(rootSlashRemoved)) { - get { - complete { - Future - .traverse(checks.toList) { c => - c.run().map(c -> _) + parameter("full" ? false) { full => + get { + def isHealthy(checkAndResults: List[(HealthCheck, HealthCheckResult)]) = + checkAndResults.forall(cr => cr._2.isValid || (!cr._1.severity.isFatal)) + val checkAndResultsFuture = Future.traverse(checks.toList) { c => + c.run().map(c -> _) + } + if (full) { + complete { + checkAndResultsFuture.map { checkAndResults => + val healthy = isHealthy(checkAndResults) + statusCode(healthy) -> ResponseJson( + status(healthy), + checkAndResults.map { + case (check, result) => toResultJson(check, result) + } + ) + } } - .map { checkAndResults => - val healthy = checkAndResults.forall(cr => cr._2.isValid || (!cr._1.severity.isFatal)) - statusCode(healthy) -> ResponseJson( - status(healthy), - checkAndResults.map { - case (check, result) => toResultJson(check, result) - } - ) + } else { + complete { + checkAndResultsFuture.map { checkAndResults => + statusCode(isHealthy(checkAndResults)) -> JsonObject.empty + } } + } } } } diff --git a/core/src/test/scala/com/github/everpeace/healthchecks/HealthCheckRoutesTest.scala b/core/src/test/scala/com/github/everpeace/healthchecks/HealthCheckRoutesTest.scala index d654065..f6fd2b5 100644 --- a/core/src/test/scala/com/github/everpeace/healthchecks/HealthCheckRoutesTest.scala +++ b/core/src/test/scala/com/github/everpeace/healthchecks/HealthCheckRoutesTest.scala @@ -60,6 +60,11 @@ class HealthRoutesTest val ok2 = healthCheck("test2")(healthy) Get("/health") ~> HealthCheckRoutes.health(ok1, ok2) ~> check { + status shouldEqual OK + responseAs[String] shouldEqual "{}" + } + + Get("/health?full=true") ~> HealthCheckRoutes.health(ok1, ok2) ~> check { status shouldEqual OK responseAs[String] shouldEqual """ @@ -81,6 +86,11 @@ class HealthRoutesTest healthCheck("test2", Severity.NonFatal)(unhealthy("error")) Get("/health") ~> HealthCheckRoutes.health(ok1, failedButNonFatal) ~> check { + status shouldEqual OK + responseAs[String] shouldEqual "{}" + } + + Get("/health?full=true") ~> HealthCheckRoutes.health(ok1, failedButNonFatal) ~> check { status shouldEqual OK responseAs[String] shouldEqual """ @@ -103,6 +113,11 @@ class HealthRoutesTest val failedFatal = healthCheck("test3")(throw new Exception("exception")) Get("/health") ~> HealthCheckRoutes.health(ok, failedButNonFatal, failedFatal) ~> check { + status shouldEqual InternalServerError + responseAs[String] shouldEqual "{}" + } + + Get("/health?full=true") ~> HealthCheckRoutes.health(ok, failedButNonFatal, failedFatal) ~> check { status shouldEqual InternalServerError responseAs[String] shouldEqual """