From 6783788e3b615ab4683293c13e87c0fbb666ae25 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 13 Nov 2023 15:50:35 -0600 Subject: [PATCH] Add a new addCheck variant allowing the caller to set the health check name; add tests; revise doc Signed-off-by: Tim Quinn --- docs/se/health.adoc | 34 +++++----- .../observe/health/HealthObserverSupport.java | 29 +++++++++ .../health/TestNamedResponseSupplier.java | 63 +++++++++++++++++++ 3 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 webserver/observe/health/src/test/java/io/helidon/webserver/observe/health/TestNamedResponseSupplier.java diff --git a/docs/se/health.adoc b/docs/se/health.adoc index a1ee873b738..50238bcb359 100644 --- a/docs/se/health.adoc +++ b/docs/se/health.adoc @@ -93,9 +93,9 @@ A health check is a Java functional interface that returns a [source,java] .Health check with a lambda expression: ---- -HealthCheck hc = () -> HealthCheckResponse +Supplier hcr = () -> HealthCheckResponse .builder() - .detail("exampleCheck", "looking-good") + .detail("exampleValue", "looking-good") .status(HealthCheckResponse.Status.UP) // always "up" .build(); ---- @@ -106,12 +106,12 @@ HealthCheck hc = () -> HealthCheckResponse static HealthCheckResponse exampleHealthCheck() { return HealthCheckResponse .builder() - .detail("exampleHealthCheck", "looking-good") + .detail("exampleValue", "looking-good") .status(HealthCheckResponse.Status.UP) // always "up" .build(); } -HealthCheck hc = Main::exampleHealthCheck; +Supplier hcr = Main::exampleHealthCheck; ---- A real health check would compute its status dynamically based on the conditions in the server rather than hard-coding a fixed value. @@ -148,18 +148,24 @@ instance of `HealthObserver`: ---- WebServer server = WebServer.builder() .config(config.get("server")) - .addFeature(ObserveFeature.create(HealthObserver.builder() // <1> - .addCheck(hc) // <2> - .details(true) // <3> - .build())) // <4> + .addFeature(ObserveFeature.create(HealthObserver.builder() // <1> + .addCheck(hcr, // <2> + HealthCheckType.READINESS, // <3> + "exampleCheck") // <4> + .details(true) // <5> + .build())) // <6> .routing(Main::routing) .build() .start(); ---- <1> Create a builder for the `HealthObserver`. -<2> Add the custom health check to the `HealthObserver.Builder`. Invoke `addCheck` multiple times to add multiple custom health checks. -<3> Set detailed HTTP response output to `true`. -<4> Build the `HealthObserver` so it can be added to the observability feature that is registered with the webserver. +<2> Create and add a custom health check to the builder, passing the supplier of the health check response (the `hcr` variable in this example). +<3> Set the type of health check. +<4> Set the name of the health check. +<5> Set the health observer to include details in the output. (optional) +<6> Build the `HealthObserver` so it can be added to the observability feature that is registered with the webserver. + +Invoke `addCheck` multiple times to add multiple custom health checks. Here is a sample response to the custom health check registered above: @@ -169,11 +175,11 @@ Here is a sample response to the custom health check registered above: { "status": "UP", "checks": [ - { - "name": "Main$$Lambda/0x00000001320ac800", + { + "name": "exampleCheck", "status": "UP", "data": { - "exampleHealthCheck": "looking-good" + "exampleValue": "looking-good" } }, ... diff --git a/webserver/observe/health/src/main/java/io/helidon/webserver/observe/health/HealthObserverSupport.java b/webserver/observe/health/src/main/java/io/helidon/webserver/observe/health/HealthObserverSupport.java index 3e85c7b56f9..01acc9e1439 100644 --- a/webserver/observe/health/src/main/java/io/helidon/webserver/observe/health/HealthObserverSupport.java +++ b/webserver/observe/health/src/main/java/io/helidon/webserver/observe/health/HealthObserverSupport.java @@ -16,6 +16,8 @@ package io.helidon.webserver.observe.health; +import java.util.function.Supplier; + import io.helidon.builder.api.Prototype; import io.helidon.health.HealthCheck; import io.helidon.health.HealthCheckResponse; @@ -46,6 +48,33 @@ static void addCheck(HealthObserverConfig.BuilderBase builder, HealthCheck } } + /** + * Add a health check using the provided response supplier, type, and name. + * + * @param builder required for the custom method + * @param responseSupplier supplier of the health check response + * @param type type to use + * @param name name to use for the health check + */ + @Prototype.BuilderMethod + static void addCheck(HealthObserverConfig.BuilderBase builder, + Supplier responseSupplier, + HealthCheckType type, + String name) { + addCheck(builder, new HealthCheck() { + @Override + public HealthCheckResponse call() { + return responseSupplier.get(); + } + + @Override + public String name() { + return name; + } + }, + type); + } + /** * Add the provided health checks. * diff --git a/webserver/observe/health/src/test/java/io/helidon/webserver/observe/health/TestNamedResponseSupplier.java b/webserver/observe/health/src/test/java/io/helidon/webserver/observe/health/TestNamedResponseSupplier.java new file mode 100644 index 00000000000..3a14fe831ac --- /dev/null +++ b/webserver/observe/health/src/test/java/io/helidon/webserver/observe/health/TestNamedResponseSupplier.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.helidon.webserver.observe.health; + +import java.util.Optional; + +import io.helidon.common.testing.junit5.OptionalMatcher; +import io.helidon.health.HealthCheck; +import io.helidon.health.HealthCheckResponse; +import io.helidon.health.HealthCheckType; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; + +class TestNamedResponseSupplier { + + + private static final String NAMED_CHECK_NAME = "testCheck"; + @Test + void checkForName() { + HealthObserver healthObserver = HealthObserver.builder() + .addCheck(() -> HealthCheckResponse.builder() // Tests the newly-added method for fixing issue #7827 + .status(true) + .build(), + HealthCheckType.READINESS, + NAMED_CHECK_NAME) + .addCheck(() -> HealthCheckResponse.builder() // Tests the pre-existing behavior + .status(true) + .build(), + HealthCheckType.LIVENESS) + .build(); + + Optional namedCheck = healthObserver.prototype() + .healthChecks() + .stream() + .filter(hc -> hc.name().equals(NAMED_CHECK_NAME)) + .findFirst(); + + assertThat("Named check via supplier", namedCheck, OptionalMatcher.optionalPresent()); + + Optional unnamedCheck = healthObserver.prototype() + .healthChecks() + .stream() + .filter(hc -> !hc.name().equals(NAMED_CHECK_NAME)) + .findFirst(); + + assertThat("Unnamed check via supplier", unnamedCheck, OptionalMatcher.optionalPresent()); + } +}