From ee19d36fd597f6185bb16d7635625e9474a8b2fe Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Fri, 27 Oct 2017 01:22:34 +0900 Subject: [PATCH] response body become empty json object by default for performance. instead, '?full=true' parameter is introduced. --- README.md | 4 +- .../route/HealthCheckRoutes.scala | 45 ++++++++++++------- .../healthchecks/HealthCheckRoutesTest.scala | 15 +++++++ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a83ec46..6490c5b 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,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 """