From 6cbf622d13e489153fde335cb3707efdd23a1afc Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Tue, 9 Apr 2024 12:02:53 -0700 Subject: [PATCH] feat: health indicator for ext services --- .../health/BcRegistryApiHealthIndicator.java | 52 ++++++++++++++ .../health/CanadaPostApiHealthIndicator.java | 58 +++++++++++++++ .../gov/app/health/ChesHealthIndicator.java | 71 +++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 backend/src/main/java/ca/bc/gov/app/health/BcRegistryApiHealthIndicator.java create mode 100644 backend/src/main/java/ca/bc/gov/app/health/CanadaPostApiHealthIndicator.java create mode 100644 backend/src/main/java/ca/bc/gov/app/health/ChesHealthIndicator.java diff --git a/backend/src/main/java/ca/bc/gov/app/health/BcRegistryApiHealthIndicator.java b/backend/src/main/java/ca/bc/gov/app/health/BcRegistryApiHealthIndicator.java new file mode 100644 index 0000000000..f42ff2484e --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/health/BcRegistryApiHealthIndicator.java @@ -0,0 +1,52 @@ +package ca.bc.gov.app.health; + +import io.micrometer.observation.annotation.Observed; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@Component +@Slf4j +@Observed +public class BcRegistryApiHealthIndicator implements HealthIndicator { + + private final WebClient addressCompleteApi; + private Health apiHealth = Health.unknown().build(); + + public BcRegistryApiHealthIndicator( + @Qualifier("bcRegistryApi") WebClient addressCompleteApi + ) { + this.addressCompleteApi = addressCompleteApi; + } + + @Override + public Health health() { + addressCompleteApi + .get() + .uri(uriBuilder -> + uriBuilder + .path("/registry-search/api/v1/businesses/search/suggest") + .queryParam("query", "value:XX000000") + .build(Map.of()) + ) + .exchangeToMono(clientResponse -> { + if (clientResponse.statusCode().is2xxSuccessful()) { + return Mono.just(Health.up().build()); + } else { + return Mono.just(Health.down().build()); + } + }) + .doOnNext(health -> log.info("BC Registry API health: {}", health)) + .subscribe( + health -> apiHealth = health, + error -> apiHealth = Health.down().withException(error).build() + ); + log.info("Checking Bc Registry API health"); + return apiHealth; + } +} diff --git a/backend/src/main/java/ca/bc/gov/app/health/CanadaPostApiHealthIndicator.java b/backend/src/main/java/ca/bc/gov/app/health/CanadaPostApiHealthIndicator.java new file mode 100644 index 0000000000..8cb84d14e1 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/health/CanadaPostApiHealthIndicator.java @@ -0,0 +1,58 @@ +package ca.bc.gov.app.health; + +import ca.bc.gov.app.configuration.ForestClientConfiguration; +import io.micrometer.observation.annotation.Observed; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@Component +@Slf4j +@Observed +public class CanadaPostApiHealthIndicator implements HealthIndicator { + + private final WebClient addressCompleteApi; + private final ForestClientConfiguration.AddressCompleteConfiguration configuration; + private Health apiHealth = Health.unknown().build(); + + public CanadaPostApiHealthIndicator( + ForestClientConfiguration configuration, + @Qualifier("addressCompleteApi") WebClient addressCompleteApi + ) { + this.configuration = configuration.getAddressComplete(); + this.addressCompleteApi = addressCompleteApi; + } + + @Override + public Health health() { + addressCompleteApi + .get() + .uri(uriBuilder -> + uriBuilder + .path("/find/v2.10/json3.ws") + .queryParam("key", configuration.getApiKey()) + .build(Map.of()) + ) + .exchangeToMono(clientResponse -> { + if (clientResponse.statusCode().is2xxSuccessful()) { + return Mono.just(Health.up().build()); + } else { + return Mono.just(Health.down().build()); + } + }) + .doOnNext(health -> log.info("Canada Post API health: {}", health)) + .subscribe( + health -> apiHealth = health, + error -> apiHealth = Health.down().withException(error).build() + ); + log.info("Checking Canada Post API health"); + + + return apiHealth; + } +} \ No newline at end of file diff --git a/backend/src/main/java/ca/bc/gov/app/health/ChesHealthIndicator.java b/backend/src/main/java/ca/bc/gov/app/health/ChesHealthIndicator.java new file mode 100644 index 0000000000..e20a0eef00 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/health/ChesHealthIndicator.java @@ -0,0 +1,71 @@ +package ca.bc.gov.app.health; + +import ca.bc.gov.app.configuration.ForestClientConfiguration; +import ca.bc.gov.app.dto.ches.CommonExposureJwtDto; +import io.micrometer.observation.annotation.Observed; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@Component +@Slf4j +@Observed +public class ChesHealthIndicator implements HealthIndicator { + + private final WebClient chesApi; + + private final WebClient authApi; + private Health apiHealth = Health.unknown().build(); + + public ChesHealthIndicator( + @Qualifier("chesApi") WebClient chesApi, + @Qualifier("authApi") WebClient authApi + ) { + this.chesApi = chesApi; + this.authApi = authApi; + } + + @Override + public Health health() { + getToken() + .flatMap(token -> + chesApi + .get() + .uri("/health") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + token) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .exchangeToMono(clientResponse -> { + if (clientResponse.statusCode().is2xxSuccessful()) { + return Mono.just(Health.up().build()); + } else { + return Mono.just(Health.down().build()); + } + }) + ) + .doOnNext(health -> log.info("CHES API health: {}", health)) + .subscribe( + health -> apiHealth = health, + error -> apiHealth = Health.down().withException(error).build() + ); + log.info("Checking CHES API health"); + return apiHealth; + } + + private Mono getToken() { + return + authApi + .post() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .body(BodyInserters.fromFormData("grant_type", "client_credentials")) + .retrieve() + .bodyToMono(CommonExposureJwtDto.class) + .map(CommonExposureJwtDto::accessToken); + } +}