From 784db2622aabb1aa0551d4e14560015b763a959b Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 10:03:28 -0400 Subject: [PATCH 01/16] add in separate okta component --- .../BackendAndDatabaseHealthIndicator.java | 14 ---------- .../api/heathcheck/OktaHealthIndicator.java | 28 +++++++++++++++++++ .../idp/repository/DemoOktaRepository.java | 2 +- .../idp/repository/LiveOktaRepository.java | 4 ++- backend/src/main/resources/application.yaml | 5 ++++ ...BackendAndDatabaseHealthIndicatorTest.java | 2 +- 6 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java index bb077eadff..7a7b8e403b 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java @@ -1,8 +1,6 @@ package gov.cdc.usds.simplereport.api.heathcheck; -import com.okta.sdk.resource.client.ApiException; import gov.cdc.usds.simplereport.db.repository.FeatureFlagRepository; -import gov.cdc.usds.simplereport.idp.repository.OktaRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.JDBCConnectionException; @@ -15,28 +13,16 @@ @RequiredArgsConstructor public class BackendAndDatabaseHealthIndicator implements HealthIndicator { private final FeatureFlagRepository _ffRepo; - private final OktaRepository _oktaRepo; - public static final String ACTIVE_LITERAL = "ACTIVE"; @Override public Health health() { try { _ffRepo.findAll(); - String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); - - if (!ACTIVE_LITERAL.equals(oktaStatus)) { - log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); - return Health.down().build(); - } return Health.up().build(); // reach into the ff repository returned a bad value or db connection issue respectively } catch (IllegalArgumentException | JDBCConnectionException e) { return Health.down().build(); - } catch (ApiException e) { - // Okta API call errored - log.info(e.getMessage()); - return Health.down().build(); } } } diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java new file mode 100644 index 0000000000..045035a6e1 --- /dev/null +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java @@ -0,0 +1,28 @@ +package gov.cdc.usds.simplereport.api.heathcheck; + +import gov.cdc.usds.simplereport.idp.repository.OktaRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class OktaHealthIndicator implements HealthIndicator { + private final OktaRepository _oktaRepo; + public static final String ACTIVE_LITERAL = "ACTIVE"; + + @Override + public Health health() { + String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); + if (!ACTIVE_LITERAL.equals(oktaStatus)) { + log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + + return oktaDegradedWarning.build(); + } + return Health.up().build(); + } +} diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java index 01eb007649..9cafca9de3 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java @@ -1,6 +1,6 @@ package gov.cdc.usds.simplereport.idp.repository; -import static gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator.ACTIVE_LITERAL; +import static gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator.ACTIVE_LITERAL; import com.okta.sdk.resource.model.UserStatus; import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java index 9ecb41ecaf..ac6ee83419 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java @@ -690,7 +690,9 @@ public PartialOktaUser findUser(String username) { @Override public String getApplicationStatusForHealthCheck() { - return app.getStatus().toString(); + // return Objects.requireNonNull(app.getStatus()).toString(); + // Demo hardcode return to test on a lower. + return "INACTIVE"; } private Optional getOrganizationRoleClaimsFromAuthorities( diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index 477bda79d2..440e0cf747 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -78,6 +78,11 @@ management: endpoint.info.enabled: true endpoints.web.exposure.include: health, info endpoint.health.show-components: always + endpoint: + health: + status: + http-mapping: + okta_degraded: 204 okta: oauth2: issuer: https://hhs-prime.okta.com/oauth2/default diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java index 9e0cfa936c..7d958305a8 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java @@ -1,6 +1,6 @@ package gov.cdc.usds.simplereport.api.healthcheck; -import static gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator.ACTIVE_LITERAL; +import static gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator.ACTIVE_LITERAL; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; From 04ef11b048adbb42bc9d3bdaff15a38c78e98464 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 10:49:31 -0400 Subject: [PATCH 02/16] add in okta messages --- frontend/deploy-smoke.js | 5 +-- frontend/src/app/DeploySmokeTest.tsx | 65 +++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/frontend/deploy-smoke.js b/frontend/deploy-smoke.js index d252ebb7e9..1615365051 100644 --- a/frontend/deploy-smoke.js +++ b/frontend/deploy-smoke.js @@ -3,7 +3,6 @@ // endpoint which does a simple ping to a non-sensitive DB table to verify // all the connections are good. // https://github.com/CDCgov/prime-simplereport/pull/7057 - require("dotenv").config(); let { Builder } = require("selenium-webdriver"); const Chrome = require("selenium-webdriver/chrome"); @@ -33,12 +32,12 @@ driver return value; }) .then((value) => { - if (value.includes("success")) { + if (value.includes("App status returned success")) { console.log(`Smoke test returned success status for ${appUrl}`); process.exitCode = 0; return; } - if (value.includes("failure")) { + if (value.includes("App status returned failure")) { console.log(`Smoke test returned failure status for ${appUrl}`); process.exitCode = 1; return; diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx index 1ddcbc3e9e..12e4cb3331 100644 --- a/frontend/src/app/DeploySmokeTest.tsx +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -2,26 +2,77 @@ import { useEffect, useState } from "react"; import FetchClient from "./utils/api"; +const APP_STATUS_LOADING = "App status loading..."; +const APP_STATUS_SUCCESS = "App status returned success :)"; +const APP_STATUS_FAILURE = "App status returned failure :("; + +const OKTA_STATUS_LOADING = "Okta status loading..."; +const OKTA_STATUS_SUCCESS = "Okta status returned success :)"; +const OKTA_STATUS_FAILURE = "Okta status returned failure :("; + const api = new FetchClient(undefined, { mode: "cors" }); const DeploySmokeTest = (): JSX.Element => { - const [success, setSuccess] = useState(); + const [appStatus, setAppStatus] = useState(); + const [oktaStatus, setOktaStatus] = useState(); + useEffect(() => { api .getRequest("/actuator/health/backend-and-db-smoke-test") .then((response) => { const status = JSON.parse(response); - if (status.status === "UP") return setSuccess(true); - setSuccess(false); + if (status.status === "UP") return setAppStatus(true); + setAppStatus(false); + }) + .catch((e) => { + console.error(e); + setAppStatus(false); + }); + }, []); + + useEffect(() => { + api + .getRequest("/actuator/health") + .then((response) => { + const status = JSON.parse(response); + if (status.okta === "UP") return setOktaStatus(true); + setOktaStatus(false); }) .catch((e) => { console.error(e); - setSuccess(false); + setOktaStatus(false); }); }, []); - if (success === undefined) return <>Status loading...; - if (success) return <> Status returned success :) ; - return <> Status returned failure :( ; + let appStatusMessage; + switch (appStatus) { + case undefined: + appStatusMessage = APP_STATUS_LOADING; + break; + case true: + appStatusMessage = APP_STATUS_SUCCESS; + break; + case false: + appStatusMessage = APP_STATUS_FAILURE; + break; + } + let oktaStatusMessage; + switch (oktaStatus) { + case undefined: + oktaStatusMessage = OKTA_STATUS_LOADING; + break; + case true: + oktaStatusMessage = OKTA_STATUS_SUCCESS; + break; + case false: + oktaStatusMessage = OKTA_STATUS_FAILURE; + break; + } + return ( + <> +
{appStatusMessage}
+
{oktaStatusMessage}
+ + ); }; export default DeploySmokeTest; From a37f4a7526887efa3a7411737009772040ffa041 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 11:30:18 -0400 Subject: [PATCH 03/16] add in tests --- ...BackendAndDatabaseHealthIndicatorTest.java | 10 ----- .../healthcheck/OktaHealthIndicatorTest.java | 37 +++++++++++++++++++ .../test_util/SliceTestConfiguration.java | 2 + 3 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java index 7d958305a8..44064a9510 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java @@ -1,13 +1,11 @@ package gov.cdc.usds.simplereport.api.healthcheck; -import static gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator.ACTIVE_LITERAL; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; import gov.cdc.usds.simplereport.db.repository.BaseRepositoryTest; import gov.cdc.usds.simplereport.db.repository.FeatureFlagRepository; -import gov.cdc.usds.simplereport.idp.repository.OktaRepository; import java.sql.SQLException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -23,14 +21,12 @@ class BackendAndDatabaseHealthIndicatorTest extends BaseRepositoryTest { @SpyBean private FeatureFlagRepository mockFeatureFlagRepo; - @SpyBean private OktaRepository mockOktaRepo; @Autowired private BackendAndDatabaseHealthIndicator indicator; @Test void health_succeedsWhenReposDoesntThrow() { when(mockFeatureFlagRepo.findAll()).thenReturn(List.of()); - when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn(ACTIVE_LITERAL); assertThat(indicator.health()).isEqualTo(Health.up().build()); } @@ -51,10 +47,4 @@ void health_failsWhenFeatureFlagRepoThrows() { when(mockFeatureFlagRepo.findAll()).thenThrow(dbConnectionException); assertThat(indicator.health()).isEqualTo(Health.down().build()); } - - @Test - void health_failsWhenOktaRepoDoesntReturnActive() { - when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("INACTIVE"); - assertThat(indicator.health()).isEqualTo(Health.down().build()); - } } diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java new file mode 100644 index 0000000000..6932e875d3 --- /dev/null +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java @@ -0,0 +1,37 @@ +package gov.cdc.usds.simplereport.api.healthcheck; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator; +import gov.cdc.usds.simplereport.db.repository.BaseRepositoryTest; +import gov.cdc.usds.simplereport.idp.repository.DemoOktaRepository; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.mock.mockito.SpyBean; + +@RequiredArgsConstructor +@EnableConfigurationProperties +class OktaHealthIndicatorTest extends BaseRepositoryTest { + + @SpyBean private DemoOktaRepository mockOktaRepo; + + @Autowired private OktaHealthIndicator indicator; + + @Test + void health_SUCCEEDSWhenOktaRepoReturnsActive() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("ACTIVE"); + assertThat(indicator.health()).isEqualTo(Health.up().build()); + } + + @Test + void health_failsWhenOktaRepoDoesntReturnActive() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("INACTIVE"); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + + assertThat(indicator.health()).isEqualTo(oktaDegradedWarning.build()); + } +} diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java b/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java index 90d8e55b71..442dd219d0 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java @@ -6,6 +6,7 @@ import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; import gov.cdc.usds.simplereport.api.WebhookContextHolder; import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; +import gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator; import gov.cdc.usds.simplereport.api.pxp.CurrentPatientContextHolder; import gov.cdc.usds.simplereport.config.AuditingConfig; import gov.cdc.usds.simplereport.config.AuthorizationProperties; @@ -105,6 +106,7 @@ TenantDataAccessService.class, PatientSelfRegistrationLinkService.class, BackendAndDatabaseHealthIndicator.class, + OktaHealthIndicator.class, EmailService.class, SendGridDisabledConfiguration.class, }) From c9fc2f141552402dcadb44c6138f66346247c6e3 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 11:32:30 -0400 Subject: [PATCH 04/16] add in okta message --- .../BackendAndDatabaseHealthIndicator.java | 14 ---- .../api/heathcheck/OktaHealthIndicator.java | 28 ++++++++ .../idp/repository/DemoOktaRepository.java | 2 +- .../idp/repository/LiveOktaRepository.java | 4 +- backend/src/main/resources/application.yaml | 5 ++ ...BackendAndDatabaseHealthIndicatorTest.java | 10 --- .../healthcheck/OktaHealthIndicatorTest.java | 37 +++++++++++ .../test_util/SliceTestConfiguration.java | 2 + frontend/deploy-smoke.js | 5 +- frontend/src/app/DeploySmokeTest.tsx | 65 +++++++++++++++++-- 10 files changed, 136 insertions(+), 36 deletions(-) create mode 100644 backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java create mode 100644 backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java index bb077eadff..7a7b8e403b 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java @@ -1,8 +1,6 @@ package gov.cdc.usds.simplereport.api.heathcheck; -import com.okta.sdk.resource.client.ApiException; import gov.cdc.usds.simplereport.db.repository.FeatureFlagRepository; -import gov.cdc.usds.simplereport.idp.repository.OktaRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.JDBCConnectionException; @@ -15,28 +13,16 @@ @RequiredArgsConstructor public class BackendAndDatabaseHealthIndicator implements HealthIndicator { private final FeatureFlagRepository _ffRepo; - private final OktaRepository _oktaRepo; - public static final String ACTIVE_LITERAL = "ACTIVE"; @Override public Health health() { try { _ffRepo.findAll(); - String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); - - if (!ACTIVE_LITERAL.equals(oktaStatus)) { - log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); - return Health.down().build(); - } return Health.up().build(); // reach into the ff repository returned a bad value or db connection issue respectively } catch (IllegalArgumentException | JDBCConnectionException e) { return Health.down().build(); - } catch (ApiException e) { - // Okta API call errored - log.info(e.getMessage()); - return Health.down().build(); } } } diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java new file mode 100644 index 0000000000..045035a6e1 --- /dev/null +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java @@ -0,0 +1,28 @@ +package gov.cdc.usds.simplereport.api.heathcheck; + +import gov.cdc.usds.simplereport.idp.repository.OktaRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class OktaHealthIndicator implements HealthIndicator { + private final OktaRepository _oktaRepo; + public static final String ACTIVE_LITERAL = "ACTIVE"; + + @Override + public Health health() { + String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); + if (!ACTIVE_LITERAL.equals(oktaStatus)) { + log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + + return oktaDegradedWarning.build(); + } + return Health.up().build(); + } +} diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java index 01eb007649..9cafca9de3 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java @@ -1,6 +1,6 @@ package gov.cdc.usds.simplereport.idp.repository; -import static gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator.ACTIVE_LITERAL; +import static gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator.ACTIVE_LITERAL; import com.okta.sdk.resource.model.UserStatus; import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java index 9ecb41ecaf..ac6ee83419 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java @@ -690,7 +690,9 @@ public PartialOktaUser findUser(String username) { @Override public String getApplicationStatusForHealthCheck() { - return app.getStatus().toString(); + // return Objects.requireNonNull(app.getStatus()).toString(); + // Demo hardcode return to test on a lower. + return "INACTIVE"; } private Optional getOrganizationRoleClaimsFromAuthorities( diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index 477bda79d2..440e0cf747 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -78,6 +78,11 @@ management: endpoint.info.enabled: true endpoints.web.exposure.include: health, info endpoint.health.show-components: always + endpoint: + health: + status: + http-mapping: + okta_degraded: 204 okta: oauth2: issuer: https://hhs-prime.okta.com/oauth2/default diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java index 9e0cfa936c..44064a9510 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java @@ -1,13 +1,11 @@ package gov.cdc.usds.simplereport.api.healthcheck; -import static gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator.ACTIVE_LITERAL; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; import gov.cdc.usds.simplereport.db.repository.BaseRepositoryTest; import gov.cdc.usds.simplereport.db.repository.FeatureFlagRepository; -import gov.cdc.usds.simplereport.idp.repository.OktaRepository; import java.sql.SQLException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -23,14 +21,12 @@ class BackendAndDatabaseHealthIndicatorTest extends BaseRepositoryTest { @SpyBean private FeatureFlagRepository mockFeatureFlagRepo; - @SpyBean private OktaRepository mockOktaRepo; @Autowired private BackendAndDatabaseHealthIndicator indicator; @Test void health_succeedsWhenReposDoesntThrow() { when(mockFeatureFlagRepo.findAll()).thenReturn(List.of()); - when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn(ACTIVE_LITERAL); assertThat(indicator.health()).isEqualTo(Health.up().build()); } @@ -51,10 +47,4 @@ void health_failsWhenFeatureFlagRepoThrows() { when(mockFeatureFlagRepo.findAll()).thenThrow(dbConnectionException); assertThat(indicator.health()).isEqualTo(Health.down().build()); } - - @Test - void health_failsWhenOktaRepoDoesntReturnActive() { - when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("INACTIVE"); - assertThat(indicator.health()).isEqualTo(Health.down().build()); - } } diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java new file mode 100644 index 0000000000..6932e875d3 --- /dev/null +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java @@ -0,0 +1,37 @@ +package gov.cdc.usds.simplereport.api.healthcheck; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator; +import gov.cdc.usds.simplereport.db.repository.BaseRepositoryTest; +import gov.cdc.usds.simplereport.idp.repository.DemoOktaRepository; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.mock.mockito.SpyBean; + +@RequiredArgsConstructor +@EnableConfigurationProperties +class OktaHealthIndicatorTest extends BaseRepositoryTest { + + @SpyBean private DemoOktaRepository mockOktaRepo; + + @Autowired private OktaHealthIndicator indicator; + + @Test + void health_SUCCEEDSWhenOktaRepoReturnsActive() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("ACTIVE"); + assertThat(indicator.health()).isEqualTo(Health.up().build()); + } + + @Test + void health_failsWhenOktaRepoDoesntReturnActive() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("INACTIVE"); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + + assertThat(indicator.health()).isEqualTo(oktaDegradedWarning.build()); + } +} diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java b/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java index 90d8e55b71..442dd219d0 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java @@ -6,6 +6,7 @@ import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; import gov.cdc.usds.simplereport.api.WebhookContextHolder; import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; +import gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator; import gov.cdc.usds.simplereport.api.pxp.CurrentPatientContextHolder; import gov.cdc.usds.simplereport.config.AuditingConfig; import gov.cdc.usds.simplereport.config.AuthorizationProperties; @@ -105,6 +106,7 @@ TenantDataAccessService.class, PatientSelfRegistrationLinkService.class, BackendAndDatabaseHealthIndicator.class, + OktaHealthIndicator.class, EmailService.class, SendGridDisabledConfiguration.class, }) diff --git a/frontend/deploy-smoke.js b/frontend/deploy-smoke.js index d252ebb7e9..1615365051 100644 --- a/frontend/deploy-smoke.js +++ b/frontend/deploy-smoke.js @@ -3,7 +3,6 @@ // endpoint which does a simple ping to a non-sensitive DB table to verify // all the connections are good. // https://github.com/CDCgov/prime-simplereport/pull/7057 - require("dotenv").config(); let { Builder } = require("selenium-webdriver"); const Chrome = require("selenium-webdriver/chrome"); @@ -33,12 +32,12 @@ driver return value; }) .then((value) => { - if (value.includes("success")) { + if (value.includes("App status returned success")) { console.log(`Smoke test returned success status for ${appUrl}`); process.exitCode = 0; return; } - if (value.includes("failure")) { + if (value.includes("App status returned failure")) { console.log(`Smoke test returned failure status for ${appUrl}`); process.exitCode = 1; return; diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx index 1ddcbc3e9e..12e4cb3331 100644 --- a/frontend/src/app/DeploySmokeTest.tsx +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -2,26 +2,77 @@ import { useEffect, useState } from "react"; import FetchClient from "./utils/api"; +const APP_STATUS_LOADING = "App status loading..."; +const APP_STATUS_SUCCESS = "App status returned success :)"; +const APP_STATUS_FAILURE = "App status returned failure :("; + +const OKTA_STATUS_LOADING = "Okta status loading..."; +const OKTA_STATUS_SUCCESS = "Okta status returned success :)"; +const OKTA_STATUS_FAILURE = "Okta status returned failure :("; + const api = new FetchClient(undefined, { mode: "cors" }); const DeploySmokeTest = (): JSX.Element => { - const [success, setSuccess] = useState(); + const [appStatus, setAppStatus] = useState(); + const [oktaStatus, setOktaStatus] = useState(); + useEffect(() => { api .getRequest("/actuator/health/backend-and-db-smoke-test") .then((response) => { const status = JSON.parse(response); - if (status.status === "UP") return setSuccess(true); - setSuccess(false); + if (status.status === "UP") return setAppStatus(true); + setAppStatus(false); + }) + .catch((e) => { + console.error(e); + setAppStatus(false); + }); + }, []); + + useEffect(() => { + api + .getRequest("/actuator/health") + .then((response) => { + const status = JSON.parse(response); + if (status.okta === "UP") return setOktaStatus(true); + setOktaStatus(false); }) .catch((e) => { console.error(e); - setSuccess(false); + setOktaStatus(false); }); }, []); - if (success === undefined) return <>Status loading...; - if (success) return <> Status returned success :) ; - return <> Status returned failure :( ; + let appStatusMessage; + switch (appStatus) { + case undefined: + appStatusMessage = APP_STATUS_LOADING; + break; + case true: + appStatusMessage = APP_STATUS_SUCCESS; + break; + case false: + appStatusMessage = APP_STATUS_FAILURE; + break; + } + let oktaStatusMessage; + switch (oktaStatus) { + case undefined: + oktaStatusMessage = OKTA_STATUS_LOADING; + break; + case true: + oktaStatusMessage = OKTA_STATUS_SUCCESS; + break; + case false: + oktaStatusMessage = OKTA_STATUS_FAILURE; + break; + } + return ( + <> +
{appStatusMessage}
+
{oktaStatusMessage}
+ + ); }; export default DeploySmokeTest; From 94fecbd8836af8a16fc29337d1d40063df671781 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 11:33:26 -0400 Subject: [PATCH 05/16] undo hardcode for test branch --- .../usds/simplereport/idp/repository/LiveOktaRepository.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java index ac6ee83419..113c2c9faa 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java @@ -39,6 +39,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -690,9 +691,7 @@ public PartialOktaUser findUser(String username) { @Override public String getApplicationStatusForHealthCheck() { - // return Objects.requireNonNull(app.getStatus()).toString(); - // Demo hardcode return to test on a lower. - return "INACTIVE"; + return Objects.requireNonNull(app.getStatus()).toString(); } private Optional getOrganizationRoleClaimsFromAuthorities( From d130f3c8e694d98881a0f4506851151926d1d86a Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 12:13:24 -0400 Subject: [PATCH 06/16] fix frontend test --- frontend/src/app/DeploySmokeTest.test.tsx | 21 +++++++++++++++------ frontend/src/app/DeploySmokeTest.tsx | 12 ++++++------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx index 0d3a8cbde0..db7ff9a834 100644 --- a/frontend/src/app/DeploySmokeTest.test.tsx +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -1,7 +1,11 @@ import { render, screen, waitFor } from "@testing-library/react"; import { FetchMock } from "jest-fetch-mock"; -import DeploySmokeTest from "./DeploySmokeTest"; +import DeploySmokeTest, { + APP_STATUS_FAILURE, + APP_STATUS_LOADING, + APP_STATUS_SUCCESS, +} from "./DeploySmokeTest"; describe("DeploySmokeTest", () => { beforeEach(() => { @@ -10,21 +14,26 @@ describe("DeploySmokeTest", () => { it("renders success when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); + (fetch as FetchMock).mockResponseOnce( + JSON.stringify({ okta: "UP", status: "UP" }) + ); render(); await waitFor(() => - expect(screen.queryByText("Status loading...")).not.toBeInTheDocument() + expect(screen.queryByText(APP_STATUS_LOADING)).not.toBeInTheDocument() ); - expect(screen.getByText("Status returned success :)")); + expect(screen.getByText(APP_STATUS_SUCCESS)); }); it("renders failure when returned from the API endpoint", async () => { - (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "DOWN" })); + (fetch as FetchMock).mockResponse( + JSON.stringify({ okta: "UP", status: "DOWN" }) + ); render(); await waitFor(() => - expect(screen.queryByText("Status loading...")).not.toBeInTheDocument() + expect(screen.queryByText(APP_STATUS_LOADING)).not.toBeInTheDocument() ); - expect(screen.getByText("Status returned failure :(")); + expect(screen.getByText(APP_STATUS_FAILURE)); }); }); diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx index 12e4cb3331..a48433b351 100644 --- a/frontend/src/app/DeploySmokeTest.tsx +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -2,13 +2,13 @@ import { useEffect, useState } from "react"; import FetchClient from "./utils/api"; -const APP_STATUS_LOADING = "App status loading..."; -const APP_STATUS_SUCCESS = "App status returned success :)"; -const APP_STATUS_FAILURE = "App status returned failure :("; +export const APP_STATUS_LOADING = "App status loading..."; +export const APP_STATUS_SUCCESS = "App status returned success :)"; +export const APP_STATUS_FAILURE = "App status returned failure :("; -const OKTA_STATUS_LOADING = "Okta status loading..."; -const OKTA_STATUS_SUCCESS = "Okta status returned success :)"; -const OKTA_STATUS_FAILURE = "Okta status returned failure :("; +export const OKTA_STATUS_LOADING = "Okta status loading..."; +export const OKTA_STATUS_SUCCESS = "Okta status returned success :)"; +export const OKTA_STATUS_FAILURE = "Okta status returned failure :("; const api = new FetchClient(undefined, { mode: "cors" }); const DeploySmokeTest = (): JSX.Element => { From 9c6d3e7084a471b0482fcd888cc8ab3a9653dae0 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 12:13:24 -0400 Subject: [PATCH 07/16] fix frontend test --- frontend/src/app/DeploySmokeTest.test.tsx | 21 +++++++++++++++------ frontend/src/app/DeploySmokeTest.tsx | 12 ++++++------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx index 0d3a8cbde0..db7ff9a834 100644 --- a/frontend/src/app/DeploySmokeTest.test.tsx +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -1,7 +1,11 @@ import { render, screen, waitFor } from "@testing-library/react"; import { FetchMock } from "jest-fetch-mock"; -import DeploySmokeTest from "./DeploySmokeTest"; +import DeploySmokeTest, { + APP_STATUS_FAILURE, + APP_STATUS_LOADING, + APP_STATUS_SUCCESS, +} from "./DeploySmokeTest"; describe("DeploySmokeTest", () => { beforeEach(() => { @@ -10,21 +14,26 @@ describe("DeploySmokeTest", () => { it("renders success when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); + (fetch as FetchMock).mockResponseOnce( + JSON.stringify({ okta: "UP", status: "UP" }) + ); render(); await waitFor(() => - expect(screen.queryByText("Status loading...")).not.toBeInTheDocument() + expect(screen.queryByText(APP_STATUS_LOADING)).not.toBeInTheDocument() ); - expect(screen.getByText("Status returned success :)")); + expect(screen.getByText(APP_STATUS_SUCCESS)); }); it("renders failure when returned from the API endpoint", async () => { - (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "DOWN" })); + (fetch as FetchMock).mockResponse( + JSON.stringify({ okta: "UP", status: "DOWN" }) + ); render(); await waitFor(() => - expect(screen.queryByText("Status loading...")).not.toBeInTheDocument() + expect(screen.queryByText(APP_STATUS_LOADING)).not.toBeInTheDocument() ); - expect(screen.getByText("Status returned failure :(")); + expect(screen.getByText(APP_STATUS_FAILURE)); }); }); diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx index 12e4cb3331..a48433b351 100644 --- a/frontend/src/app/DeploySmokeTest.tsx +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -2,13 +2,13 @@ import { useEffect, useState } from "react"; import FetchClient from "./utils/api"; -const APP_STATUS_LOADING = "App status loading..."; -const APP_STATUS_SUCCESS = "App status returned success :)"; -const APP_STATUS_FAILURE = "App status returned failure :("; +export const APP_STATUS_LOADING = "App status loading..."; +export const APP_STATUS_SUCCESS = "App status returned success :)"; +export const APP_STATUS_FAILURE = "App status returned failure :("; -const OKTA_STATUS_LOADING = "Okta status loading..."; -const OKTA_STATUS_SUCCESS = "Okta status returned success :)"; -const OKTA_STATUS_FAILURE = "Okta status returned failure :("; +export const OKTA_STATUS_LOADING = "Okta status loading..."; +export const OKTA_STATUS_SUCCESS = "Okta status returned success :)"; +export const OKTA_STATUS_FAILURE = "Okta status returned failure :("; const api = new FetchClient(undefined, { mode: "cors" }); const DeploySmokeTest = (): JSX.Element => { From 08bc5ae07fb3ec72d8c218ccfd85b73ec6eafe01 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 12:24:43 -0400 Subject: [PATCH 08/16] make status check reflect api shape for okta --- frontend/src/app/DeploySmokeTest.test.tsx | 18 ++++++++++++++++-- frontend/src/app/DeploySmokeTest.tsx | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx index db7ff9a834..a29c67e77c 100644 --- a/frontend/src/app/DeploySmokeTest.test.tsx +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -5,6 +5,8 @@ import DeploySmokeTest, { APP_STATUS_FAILURE, APP_STATUS_LOADING, APP_STATUS_SUCCESS, + OKTA_STATUS_LOADING, + OKTA_STATUS_SUCCESS, } from "./DeploySmokeTest"; describe("DeploySmokeTest", () => { @@ -15,7 +17,7 @@ describe("DeploySmokeTest", () => { it("renders success when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); (fetch as FetchMock).mockResponseOnce( - JSON.stringify({ okta: "UP", status: "UP" }) + JSON.stringify({ components: { okta: { status: "UP" } }, status: "DOWN" }) ); render(); @@ -27,7 +29,7 @@ describe("DeploySmokeTest", () => { it("renders failure when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponse( - JSON.stringify({ okta: "UP", status: "DOWN" }) + JSON.stringify({ components: { okta: { status: "UP" } }, status: "DOWN" }) ); render(); @@ -36,4 +38,16 @@ describe("DeploySmokeTest", () => { ); expect(screen.getByText(APP_STATUS_FAILURE)); }); + + it("renders Okta success when returned from the API endpoint", async () => { + (fetch as FetchMock).mockResponse( + JSON.stringify({ components: { okta: { status: "UP" } }, status: "UP" }) + ); + + render(); + await waitFor(() => + expect(screen.queryByText(OKTA_STATUS_LOADING)).not.toBeInTheDocument() + ); + expect(screen.getByText(OKTA_STATUS_SUCCESS)); + }); }); diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx index a48433b351..e52abe92f9 100644 --- a/frontend/src/app/DeploySmokeTest.tsx +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -34,7 +34,7 @@ const DeploySmokeTest = (): JSX.Element => { .getRequest("/actuator/health") .then((response) => { const status = JSON.parse(response); - if (status.okta === "UP") return setOktaStatus(true); + if (status.components.okta.status === "UP") return setOktaStatus(true); setOktaStatus(false); }) .catch((e) => { From 43e60f29f9f3ee5dc4026a37381057ca2d3f7df7 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Mon, 8 Jul 2024 12:24:43 -0400 Subject: [PATCH 09/16] make status check reflect api shape for okta --- frontend/src/app/DeploySmokeTest.test.tsx | 18 ++++++++++++++++-- frontend/src/app/DeploySmokeTest.tsx | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx index db7ff9a834..a29c67e77c 100644 --- a/frontend/src/app/DeploySmokeTest.test.tsx +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -5,6 +5,8 @@ import DeploySmokeTest, { APP_STATUS_FAILURE, APP_STATUS_LOADING, APP_STATUS_SUCCESS, + OKTA_STATUS_LOADING, + OKTA_STATUS_SUCCESS, } from "./DeploySmokeTest"; describe("DeploySmokeTest", () => { @@ -15,7 +17,7 @@ describe("DeploySmokeTest", () => { it("renders success when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); (fetch as FetchMock).mockResponseOnce( - JSON.stringify({ okta: "UP", status: "UP" }) + JSON.stringify({ components: { okta: { status: "UP" } }, status: "DOWN" }) ); render(); @@ -27,7 +29,7 @@ describe("DeploySmokeTest", () => { it("renders failure when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponse( - JSON.stringify({ okta: "UP", status: "DOWN" }) + JSON.stringify({ components: { okta: { status: "UP" } }, status: "DOWN" }) ); render(); @@ -36,4 +38,16 @@ describe("DeploySmokeTest", () => { ); expect(screen.getByText(APP_STATUS_FAILURE)); }); + + it("renders Okta success when returned from the API endpoint", async () => { + (fetch as FetchMock).mockResponse( + JSON.stringify({ components: { okta: { status: "UP" } }, status: "UP" }) + ); + + render(); + await waitFor(() => + expect(screen.queryByText(OKTA_STATUS_LOADING)).not.toBeInTheDocument() + ); + expect(screen.getByText(OKTA_STATUS_SUCCESS)); + }); }); diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx index a48433b351..e52abe92f9 100644 --- a/frontend/src/app/DeploySmokeTest.tsx +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -34,7 +34,7 @@ const DeploySmokeTest = (): JSX.Element => { .getRequest("/actuator/health") .then((response) => { const status = JSON.parse(response); - if (status.okta === "UP") return setOktaStatus(true); + if (status.components.okta.status === "UP") return setOktaStatus(true); setOktaStatus(false); }) .catch((e) => { From 02efb888c25fc76482c128f1b0b4df1ba0a2600f Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Tue, 9 Jul 2024 09:48:45 -0400 Subject: [PATCH 10/16] make tests more readable and handle NPE in health check --- .../api/heathcheck/OktaHealthIndicator.java | 14 +++-- frontend/src/app/DeploySmokeTest.test.tsx | 11 ++-- .../src/app/deploySmokeTestTestConstants.ts | 54 +++++++++++++++++++ 3 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 frontend/src/app/deploySmokeTestTestConstants.ts diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java index 045035a6e1..027efe7e10 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java @@ -16,13 +16,19 @@ public class OktaHealthIndicator implements HealthIndicator { @Override public Health health() { - String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); - if (!ACTIVE_LITERAL.equals(oktaStatus)) { - log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); + try { + String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); + if (!ACTIVE_LITERAL.equals(oktaStatus)) { + log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + return oktaDegradedWarning.build(); + } + } catch (NullPointerException e) { + log.info("Call to Okta repository status returned null"); Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); - return oktaDegradedWarning.build(); } + return Health.up().build(); } } diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx index a29c67e77c..4fb7140aac 100644 --- a/frontend/src/app/DeploySmokeTest.test.tsx +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -8,6 +8,7 @@ import DeploySmokeTest, { OKTA_STATUS_LOADING, OKTA_STATUS_SUCCESS, } from "./DeploySmokeTest"; +import { generateBackendApiHealthResponse } from "./deploySmokeTestTestConstants"; describe("DeploySmokeTest", () => { beforeEach(() => { @@ -15,9 +16,11 @@ describe("DeploySmokeTest", () => { }); it("renders success when returned from the API endpoint", async () => { - (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); (fetch as FetchMock).mockResponseOnce( - JSON.stringify({ components: { okta: { status: "UP" } }, status: "DOWN" }) + JSON.stringify(generateBackendApiHealthResponse("UP")) + ); + (fetch as FetchMock).mockResponseOnce( + JSON.stringify(generateBackendApiHealthResponse("DOWN")) ); render(); @@ -29,7 +32,7 @@ describe("DeploySmokeTest", () => { it("renders failure when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponse( - JSON.stringify({ components: { okta: { status: "UP" } }, status: "DOWN" }) + JSON.stringify(generateBackendApiHealthResponse("DOWN")) ); render(); @@ -41,7 +44,7 @@ describe("DeploySmokeTest", () => { it("renders Okta success when returned from the API endpoint", async () => { (fetch as FetchMock).mockResponse( - JSON.stringify({ components: { okta: { status: "UP" } }, status: "UP" }) + JSON.stringify(generateBackendApiHealthResponse()) ); render(); diff --git a/frontend/src/app/deploySmokeTestTestConstants.ts b/frontend/src/app/deploySmokeTestTestConstants.ts new file mode 100644 index 0000000000..df68fa9bc1 --- /dev/null +++ b/frontend/src/app/deploySmokeTestTestConstants.ts @@ -0,0 +1,54 @@ +type SpringHealthStatusValues = "UP" | "DOWN" | "OKTA_DEGRADED"; +export const generateBackendApiHealthResponse = ( + overallStatus?: SpringHealthStatusValues, + oktaStatus?: SpringHealthStatusValues +) => { + return { + status: overallStatus ?? "UP", + components: { + "backend-and-db-smoke-test": { + status: "UP", + }, + db: { + status: "UP", + components: { + metabaseDataSource: { + status: "UP", + }, + primaryDataSource: { + status: "UP", + }, + }, + }, + discoveryComposite: { + description: "Discovery Client not initialized", + status: "UNKNOWN", + components: { + discoveryClient: { + description: "Discovery Client not initialized", + status: "UNKNOWN", + }, + }, + }, + diskSpace: { + status: "UP", + }, + livenessState: { + status: "UP", + }, + okta: { + status: oktaStatus ?? "UP", + }, + ping: { + status: "UP", + }, + readinessState: { + status: "UP", + }, + refreshScope: { + status: "UP", + }, + }, + groups: ["liveness", "readiness"], + }; +}; From da5345fca42981ec2a2e23da3c4251dcdb031662 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Tue, 9 Jul 2024 09:57:48 -0400 Subject: [PATCH 11/16] make smoke test script include a check for okta --- frontend/deploy-smoke.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/deploy-smoke.js b/frontend/deploy-smoke.js index 1615365051..685d555ae7 100644 --- a/frontend/deploy-smoke.js +++ b/frontend/deploy-smoke.js @@ -32,16 +32,35 @@ driver return value; }) .then((value) => { + let appStatusSuccess, oktaStatusSuccess; if (value.includes("App status returned success")) { + appStatusSuccess = true; + } + if (value.includes("Okta status returned success")) { + oktaStatusSuccess = true; + } + if (value.includes("App status returned failure")) { + appStatusSuccess = false; + } + if (value.includes("Okta status returned failure")) { + oktaStatusSuccess = false; + } + + if (appStatusSuccess && oktaStatusSuccess) { console.log(`Smoke test returned success status for ${appUrl}`); process.exitCode = 0; return; } - if (value.includes("App status returned failure")) { + + if (appStatusSuccess === false || oktaStatusSuccess === false) { console.log(`Smoke test returned failure status for ${appUrl}`); + console.log( + `App health returned ${appStatusSuccess}, okta health returned ${oktaStatusSuccess}` + ); process.exitCode = 1; return; } + console.log("Smoke test encountered unknown failure."); console.log(`Root element value was: ${value}`); process.exitCode = 1; From dc649f057b17b81745f86650ef131c711c4e21bf Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Tue, 9 Jul 2024 09:59:59 -0400 Subject: [PATCH 12/16] woops --- .../usds/simplereport/idp/repository/LiveOktaRepository.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java index 2df432fb7f..113c2c9faa 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java @@ -691,9 +691,7 @@ public PartialOktaUser findUser(String username) { @Override public String getApplicationStatusForHealthCheck() { - // return Objects.requireNonNull(app.getStatus()).toString(); - // Demo hardcode return to test on a lower. - return "INACTIVE"; + return Objects.requireNonNull(app.getStatus()).toString(); } private Optional getOrganizationRoleClaimsFromAuthorities( From c84f4aa9248721ba3a7b24faa81acf91e1d6df69 Mon Sep 17 00:00:00 2001 From: fzhao99 Date: Tue, 9 Jul 2024 11:49:03 -0400 Subject: [PATCH 13/16] weave in config values into endpoint Co-authored-by: Merethe Hansen --- backend/src/main/resources/application.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index 440e0cf747..e097aded50 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -80,9 +80,11 @@ management: endpoint.health.show-components: always endpoint: health: + show-components: always status: http-mapping: okta_degraded: 204 + probes.enabled: true okta: oauth2: issuer: https://hhs-prime.okta.com/oauth2/default From 6308c6adc017b50d8053f0e2dde9a35c9e22a0d6 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Tue, 9 Jul 2024 12:17:54 -0400 Subject: [PATCH 14/16] add in old api exception --- .../api/heathcheck/OktaHealthIndicator.java | 8 +++++-- .../healthcheck/OktaHealthIndicatorTest.java | 22 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java index 027efe7e10..cc9f264a07 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/OktaHealthIndicator.java @@ -1,5 +1,6 @@ package gov.cdc.usds.simplereport.api.heathcheck; +import com.okta.sdk.resource.client.ApiException; import gov.cdc.usds.simplereport.idp.repository.OktaRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,16 +17,19 @@ public class OktaHealthIndicator implements HealthIndicator { @Override public Health health() { + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); try { String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); if (!ACTIVE_LITERAL.equals(oktaStatus)) { log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); - Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); return oktaDegradedWarning.build(); } } catch (NullPointerException e) { log.info("Call to Okta repository status returned null"); - Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + return oktaDegradedWarning.build(); + } catch (ApiException e) { + // Okta API call errored + log.info("Okta status call raised an exception: " + e.getMessage()); return oktaDegradedWarning.build(); } diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java index 6932e875d3..f47285eedf 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/OktaHealthIndicatorTest.java @@ -3,9 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; +import com.okta.sdk.resource.client.ApiException; import gov.cdc.usds.simplereport.api.heathcheck.OktaHealthIndicator; import gov.cdc.usds.simplereport.db.repository.BaseRepositoryTest; -import gov.cdc.usds.simplereport.idp.repository.DemoOktaRepository; +import gov.cdc.usds.simplereport.idp.repository.OktaRepository; import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -17,16 +18,31 @@ @EnableConfigurationProperties class OktaHealthIndicatorTest extends BaseRepositoryTest { - @SpyBean private DemoOktaRepository mockOktaRepo; + @SpyBean private OktaRepository mockOktaRepo; @Autowired private OktaHealthIndicator indicator; @Test - void health_SUCCEEDSWhenOktaRepoReturnsActive() { + void health_SucceedsWhenOktaRepoReturnsActive() { when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("ACTIVE"); assertThat(indicator.health()).isEqualTo(Health.up().build()); } + @Test + void health_FailsWhenOktaApiThrowsErrors() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()) + .thenThrow(new ApiException("some api error")); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + assertThat(indicator.health()).isEqualTo(oktaDegradedWarning.build()); + } + + @Test + void health_FailsWhenOktaApiThrowsNPE() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenThrow(new NullPointerException()); + Health.Builder oktaDegradedWarning = Health.status("OKTA_DEGRADED"); + assertThat(indicator.health()).isEqualTo(oktaDegradedWarning.build()); + } + @Test void health_failsWhenOktaRepoDoesntReturnActive() { when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("INACTIVE"); From 8a14849e9d60bd8540edf06e8a132e1cccb66e64 Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Tue, 9 Jul 2024 12:23:09 -0400 Subject: [PATCH 15/16] status check reflect subapi endpoint (sorry merethe) --- frontend/src/app/DeploySmokeTest.test.tsx | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx index 4fb7140aac..606b6606d0 100644 --- a/frontend/src/app/DeploySmokeTest.test.tsx +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -15,13 +15,9 @@ describe("DeploySmokeTest", () => { (fetch as FetchMock).resetMocks(); }); - it("renders success when returned from the API endpoint", async () => { - (fetch as FetchMock).mockResponseOnce( - JSON.stringify(generateBackendApiHealthResponse("UP")) - ); - (fetch as FetchMock).mockResponseOnce( - JSON.stringify(generateBackendApiHealthResponse("DOWN")) - ); + it("renders success when returned from the backend API smoke test endpoint", async () => { + (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); + (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "DOWN" })); render(); await waitFor(() => @@ -30,10 +26,8 @@ describe("DeploySmokeTest", () => { expect(screen.getByText(APP_STATUS_SUCCESS)); }); - it("renders failure when returned from the API endpoint", async () => { - (fetch as FetchMock).mockResponse( - JSON.stringify(generateBackendApiHealthResponse("DOWN")) - ); + it("renders failure when returned from the backend API smoke test endpoint", async () => { + (fetch as FetchMock).mockResponse(JSON.stringify({ status: "DOWN" })); render(); await waitFor(() => @@ -42,7 +36,7 @@ describe("DeploySmokeTest", () => { expect(screen.getByText(APP_STATUS_FAILURE)); }); - it("renders Okta success when returned from the API endpoint", async () => { + it("renders Okta success when returned from the backend API health endpoint", async () => { (fetch as FetchMock).mockResponse( JSON.stringify(generateBackendApiHealthResponse()) ); From 4cdf0b3b7fd12ee33c39f8149f028f0d16e6607f Mon Sep 17 00:00:00 2001 From: Bob Zhao Date: Tue, 9 Jul 2024 12:33:55 -0400 Subject: [PATCH 16/16] dedupe application.yaml --- backend/src/main/resources/application.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index e097aded50..156d354421 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -74,11 +74,10 @@ server: error: include-stacktrace: never management: - endpoint.health.probes.enabled: true - endpoint.info.enabled: true endpoints.web.exposure.include: health, info - endpoint.health.show-components: always endpoint: + info: + enabled: true health: show-components: always status: