From 62c1059ca14f1b3905a9d8be5790bcd8d06768cc Mon Sep 17 00:00:00 2001 From: Scott Leberknight <174812+sleberknight@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:44:42 +0000 Subject: [PATCH] Add options to skip REST resource registration * Add option to skip registration of GotErrorsResource * Add option to skip registration of ApplicationErrorResource Closes #289 Closes #290 --- .../dropwizard/error/ErrorContext.java | 18 +++++--- .../dropwizard/error/ErrorContextBuilder.java | 28 +++++++++++ .../error/ErrorContextUtilities.java | 13 ++++-- .../dropwizard/error/Jdbi3ErrorContext.java | 4 +- .../dropwizard/error/SimpleErrorContext.java | 4 +- .../error/resource/GotErrorsResource.java | 2 + .../error/ErrorContextUtilitiesTest.java | 12 ++++- .../error/Jdbi3ErrorContextTest.java | 46 +++++++++++++++++++ .../error/SimpleErrorContextTest.java | 46 +++++++++++++++++++ 9 files changed, 161 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/kiwiproject/dropwizard/error/ErrorContext.java b/src/main/java/org/kiwiproject/dropwizard/error/ErrorContext.java index 20a956b..fd9e4e8 100644 --- a/src/main/java/org/kiwiproject/dropwizard/error/ErrorContext.java +++ b/src/main/java/org/kiwiproject/dropwizard/error/ErrorContext.java @@ -12,14 +12,20 @@ * though you can build everything programmatically if you want to. *

* This acts much like a Dropwizard bundle in that it creates an {@link ApplicationErrorDao} for use anywhere in - * your application, and registers an + * your application, and (by default) registers an * {@link org.kiwiproject.dropwizard.error.resource.ApplicationErrorResource ApplicationErrorResource} which - * exposes application errors via REST. The {@link #errorDao()} method provides the DAO once an {@link ErrorContext} - * has been built by one of the build methods in {@link ErrorContextBuilder}. + * exposes application errors via REST. By default, it also registers a + * {@link org.kiwiproject.dropwizard.error.resource.GotErrorsResource GotErrorsResource}, which is a REST + * endpoint that can be used to determine if application errors are supported. If you don't want to register + * one or both of these resources, you can disable registration via the {@link ErrorContextBuilder#skipErrorsResource()} + * and {@link ErrorContextBuilder#skipGotErrorsResource()} methods. *

- * It also registers a health check that reports healthy if no errors have occurred in a configurable time window. The - * default value is the last 15 minutes. If desired, you can disable the creation of the health check by calling - * {@link ErrorContextBuilder#skipHealthCheck()} when constructing the instance. + * The {@link #errorDao()} method provides the DAO once an {@link ErrorContext} has been built by one of the + * "build" methods in {@link ErrorContextBuilder}. + *

+ * This library also registers a health check that reports healthy if no errors have occurred in a configurable time + * window. The default value is the last 15 minutes. If desired, you can disable the creation of the health check by + * calling {@link ErrorContextBuilder#skipHealthCheck()} when constructing the instance. *

* We currently support storing errors to a relational database with JDBI 3. If your application does not currently * have a database or uses something else, we also provide several other options including an in-memory H2 database, diff --git a/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextBuilder.java b/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextBuilder.java index 0f26593..85eb686 100644 --- a/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextBuilder.java +++ b/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextBuilder.java @@ -90,6 +90,8 @@ public class ErrorContextBuilder { private ServiceDetails serviceDetails; private DataStoreType dataStoreType; private boolean dataStoreTypeAlreadySet; + private boolean addErrorsResource = true; + private boolean addGotErrorsResource = true; private boolean addHealthCheck = true; private boolean addCleanupJob = true; private long timeWindowValue = TimeWindow.DEFAULT_TIME_WINDOW_MINUTES; @@ -142,6 +144,28 @@ public ErrorContextBuilder dataStoreType(DataStoreType dataStoreType) { return this; } + /** + * Configures the resulting {@link ErrorContext} so that it does not create/register an + * {@link org.kiwiproject.dropwizard.error.resource.ApplicationErrorResource ApplicationErrorResource}. + * + * @return this builder + */ + public ErrorContextBuilder skipErrorsResource() { + this.addErrorsResource = false; + return this; + } + + /** + * Configures the resulting {@link ErrorContext} so that it does not create/register a + * {@link org.kiwiproject.dropwizard.error.resource.GotErrorsResource GotErrorsResource}. + * + * @return this builder + */ + public ErrorContextBuilder skipGotErrorsResource() { + this.addGotErrorsResource = false; + return this; + } + /** * Configures the resulting {@link ErrorContext} so that it does not create/register a health check with Dropwizard. * @@ -316,6 +340,8 @@ private Jdbi3ErrorContext newJdbi3ErrorContext(Jdbi jdbi) { serviceDetails, jdbi, dataStoreType, + addErrorsResource, + addGotErrorsResource, addHealthCheck, timeWindowValue, timeWindowUnit, @@ -360,6 +386,8 @@ public ErrorContext buildWithDao(ApplicationErrorDao errorDao) { serviceDetails, errorDao, dataStoreType, + addErrorsResource, + addGotErrorsResource, addHealthCheck, timeWindowValue, timeWindowUnit, diff --git a/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextUtilities.java b/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextUtilities.java index b992319..858897b 100644 --- a/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextUtilities.java +++ b/src/main/java/org/kiwiproject/dropwizard/error/ErrorContextUtilities.java @@ -53,14 +53,21 @@ static PersistentHostInformation setPersistentHostInformationFrom(ServiceDetails static void registerResources(Environment environment, ApplicationErrorDao errorDao, - DataStoreType dataStoreType) { + DataStoreType dataStoreType, + boolean addErrorsResource, + boolean addGotErrorsResource) { checkArgumentNotNull(environment); checkArgumentNotNull(errorDao); checkArgumentNotNull(dataStoreType); - environment.jersey().register(new ApplicationErrorResource(errorDao)); - environment.jersey().register(new GotErrorsResource(dataStoreType)); + if (addErrorsResource) { + environment.jersey().register(new ApplicationErrorResource(errorDao)); + } + + if (addGotErrorsResource) { + environment.jersey().register(new GotErrorsResource(dataStoreType)); + } } static RecentErrorsHealthCheck registerRecentErrorsHealthCheckOrNull(boolean addHealthCheck, diff --git a/src/main/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContext.java b/src/main/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContext.java index 13a0396..84d23c1 100644 --- a/src/main/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContext.java +++ b/src/main/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContext.java @@ -44,6 +44,8 @@ class Jdbi3ErrorContext implements ErrorContext { ServiceDetails serviceDetails, Jdbi jdbi, DataStoreType dataStoreType, + boolean addErrorsResource, + boolean addGotErrorsResource, boolean addHealthCheck, long timeWindowValue, TemporalUnit timeWindowUnit, @@ -60,7 +62,7 @@ class Jdbi3ErrorContext implements ErrorContext { addHealthCheck, environment, errorDao, serviceDetails, timeWindowValue, timeWindowUnit); registerCleanupJobOrNull(addCleanupJob, environment, errorDao, cleanupConfig); - registerResources(environment, errorDao, dataStoreType); + registerResources(environment, errorDao, dataStoreType, addErrorsResource, addGotErrorsResource); } private static Jdbi3ApplicationErrorDao getOnDemandErrorDao(Jdbi jdbi) { diff --git a/src/main/java/org/kiwiproject/dropwizard/error/SimpleErrorContext.java b/src/main/java/org/kiwiproject/dropwizard/error/SimpleErrorContext.java index 1ee7080..b78d2ec 100644 --- a/src/main/java/org/kiwiproject/dropwizard/error/SimpleErrorContext.java +++ b/src/main/java/org/kiwiproject/dropwizard/error/SimpleErrorContext.java @@ -32,6 +32,8 @@ public SimpleErrorContext(Environment environment, ServiceDetails serviceDetails, ApplicationErrorDao errorDao, DataStoreType dataStoreType, + boolean addErrorsResource, + boolean addGotErrorsResource, boolean addHealthCheck, long timeWindowValue, TemporalUnit timeWindowUnit, @@ -48,7 +50,7 @@ public SimpleErrorContext(Environment environment, addHealthCheck, environment, errorDao, serviceDetails, timeWindowValue, timeWindowUnit); registerCleanupJobOrNull(addCleanupJob, environment, errorDao, cleanupConfig); - registerResources(environment, errorDao, dataStoreType); + registerResources(environment, errorDao, dataStoreType, addErrorsResource, addGotErrorsResource); } @Override diff --git a/src/main/java/org/kiwiproject/dropwizard/error/resource/GotErrorsResource.java b/src/main/java/org/kiwiproject/dropwizard/error/resource/GotErrorsResource.java index 6fbd37f..839edb5 100644 --- a/src/main/java/org/kiwiproject/dropwizard/error/resource/GotErrorsResource.java +++ b/src/main/java/org/kiwiproject/dropwizard/error/resource/GotErrorsResource.java @@ -12,6 +12,8 @@ /** * A simple JAX-RS resource for clients to determine if application errors are available or not. + * It can be used by clients as a generic way to determine whether, for example, a web service + * exposes its application errors. *

* Clients can check if application errors are available using an HTTP GET request that returns a * {@code 200 OK} response if available. The response entity contains information on whether the diff --git a/src/test/java/org/kiwiproject/dropwizard/error/ErrorContextUtilitiesTest.java b/src/test/java/org/kiwiproject/dropwizard/error/ErrorContextUtilitiesTest.java index bfd80b1..2533a51 100644 --- a/src/test/java/org/kiwiproject/dropwizard/error/ErrorContextUtilitiesTest.java +++ b/src/test/java/org/kiwiproject/dropwizard/error/ErrorContextUtilitiesTest.java @@ -7,6 +7,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -137,12 +138,21 @@ void setUp() { @Test void shouldRegisterResources() { - ErrorContextUtilities.registerResources(environment, errorDao, DataStoreType.SHARED); + ErrorContextUtilities.registerResources(environment, errorDao, DataStoreType.SHARED, true, true); verify(jersey).register(isA(ApplicationErrorResource.class)); verify(jersey).register(isA(GotErrorsResource.class)); verifyNoMoreInteractions(jersey); } + + @Test + void shouldNotRegisterResources_WhenTheyAreNotWanted() { + ErrorContextUtilities.registerResources(environment, errorDao, DataStoreType.SHARED, false, false); + + verify(jersey, never()).register(isA(ApplicationErrorResource.class)); + verify(jersey, never()).register(isA(GotErrorsResource.class)); + verifyNoMoreInteractions(jersey); + } } @Nested diff --git a/src/test/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContextTest.java b/src/test/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContextTest.java index 27bc718..076faff 100644 --- a/src/test/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContextTest.java +++ b/src/test/java/org/kiwiproject/dropwizard/error/Jdbi3ErrorContextTest.java @@ -4,6 +4,8 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -17,6 +19,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.kiwiproject.dropwizard.error.config.CleanupConfig; import org.kiwiproject.dropwizard.error.dao.jdbi3.Jdbi3ApplicationErrorDao; import org.kiwiproject.dropwizard.error.health.RecentErrorsHealthCheck; @@ -29,6 +33,7 @@ import org.kiwiproject.test.h2.H2FileBasedDatabase; import org.kiwiproject.test.junit.jupiter.H2Database; import org.kiwiproject.test.junit.jupiter.H2FileBasedDatabaseExtension; +import org.mockito.verification.VerificationMode; import java.time.temporal.ChronoUnit; @@ -137,6 +142,8 @@ private Jdbi3ErrorContext newContextWithHealthCheck(boolean addHealthCheck) { serviceDetails, jdbi, DataStoreType.NOT_SHARED, + true, + true, addHealthCheck, timeWindowAmount, timeWindowUnit, @@ -145,4 +152,43 @@ private Jdbi3ErrorContext newContextWithHealthCheck(boolean addHealthCheck) { ); } } + + @Nested + class Resources { + + @ParameterizedTest + @CsvSource(textBlock = """ + true, true + true, false + false, true, + false, false + """) + void shouldOptionallyRegisterResources(boolean addErrorsResource, boolean addGotErrorsResource) { + newContextWithAddResourceOptionsOf(addErrorsResource, addGotErrorsResource); + + var jersey = environment.jersey(); + verify(jersey, timesExpected(addErrorsResource)).register(isA(ApplicationErrorResource.class)); + verify(jersey, timesExpected(addGotErrorsResource)).register(isA(GotErrorsResource.class)); + verifyNoMoreInteractions(jersey); + } + + private VerificationMode timesExpected(boolean addResource) { + return addResource ? times(2) : never(); + } + + private Jdbi3ErrorContext newContextWithAddResourceOptionsOf(boolean addErrorsResource, + boolean addGotErrorsResource) { + return new Jdbi3ErrorContext(environment, + serviceDetails, + jdbi, + DataStoreType.NOT_SHARED, + addErrorsResource, + addGotErrorsResource, + true, + timeWindowAmount, + timeWindowUnit, + false, + new CleanupConfig()); + } + } } diff --git a/src/test/java/org/kiwiproject/dropwizard/error/SimpleErrorContextTest.java b/src/test/java/org/kiwiproject/dropwizard/error/SimpleErrorContextTest.java index 3a562f8..1cf0e16 100644 --- a/src/test/java/org/kiwiproject/dropwizard/error/SimpleErrorContextTest.java +++ b/src/test/java/org/kiwiproject/dropwizard/error/SimpleErrorContextTest.java @@ -3,6 +3,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -13,6 +15,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.kiwiproject.dropwizard.error.config.CleanupConfig; import org.kiwiproject.dropwizard.error.dao.ApplicationErrorDao; import org.kiwiproject.dropwizard.error.dao.jdk.NoOpApplicationErrorDao; @@ -23,6 +27,7 @@ import org.kiwiproject.dropwizard.error.resource.ApplicationErrorResource; import org.kiwiproject.dropwizard.error.resource.GotErrorsResource; import org.kiwiproject.test.dropwizard.mockito.DropwizardMockitoMocks; +import org.mockito.verification.VerificationMode; import java.time.temporal.ChronoUnit; @@ -113,6 +118,8 @@ private SimpleErrorContext newContextWithAddHealthCheckOf(boolean addHealthCheck serviceDetails, errorDao, dataStoreType, + true, + true, addHealthCheck, timeWindowAmount, timeWindowUnit, @@ -120,4 +127,43 @@ private SimpleErrorContext newContextWithAddHealthCheckOf(boolean addHealthCheck new CleanupConfig() ); } + + @Nested + class Resources { + + @ParameterizedTest + @CsvSource(textBlock = """ + true, true + true, false + false, true, + false, false + """) + void shouldOptionallyRegisterResources(boolean addErrorsResource, boolean addGotErrorsResource) { + newContextWithAddResourceOptionsOf(addErrorsResource, addGotErrorsResource); + + var jersey = environment.jersey(); + verify(jersey, timesExpected(addErrorsResource)).register(isA(ApplicationErrorResource.class)); + verify(jersey, timesExpected(addGotErrorsResource)).register(isA(GotErrorsResource.class)); + verifyNoMoreInteractions(jersey); + } + + private VerificationMode timesExpected(boolean addResource) { + return addResource ? times(1) : never(); + } + } + + private SimpleErrorContext newContextWithAddResourceOptionsOf(boolean addErrorsResource, + boolean addGotErrorsResource) { + return new SimpleErrorContext(environment, + serviceDetails, + errorDao, + dataStoreType, + addErrorsResource, + addGotErrorsResource, + true, + timeWindowAmount, + timeWindowUnit, + false, + new CleanupConfig()); + } }