From 32b18c95093291715a7dd990ae749ac6269fd4e0 Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Thu, 27 Aug 2020 12:25:25 -0400 Subject: [PATCH] Add an endpoint for getting the current state of feature flags. --- .../controllers/FeatureFlagsController.java | 15 +++++++++ .../storage/FeatureFlagsManager.java | 6 +++- .../FeatureFlagsControllerTest.java | 32 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsController.java index 731629a30..dcd7bc37f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsController.java @@ -6,15 +6,19 @@ import org.whispersystems.textsecuregcm.storage.FeatureFlagsManager; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; +import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Path("/v1/featureflag") @@ -28,6 +32,17 @@ public class FeatureFlagsController { this.authorizedTokens = authorizedTokens.stream().map(token -> token.getBytes(StandardCharsets.UTF_8)).collect(Collectors.toList()); } + @Timed + @GET + @Produces(MediaType.APPLICATION_JSON) + public Map get(@HeaderParam("Token") final String token) { + if (!isAuthorized(token)) { + throw new WebApplicationException(Response.Status.UNAUTHORIZED); + } + + return featureFlagsManager.getAllFlags(); + } + @Timed @PUT @Path("/{featureFlag}") diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/FeatureFlagsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/FeatureFlagsManager.java index 6251988e6..0a881a2e5 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/FeatureFlagsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/FeatureFlagsManager.java @@ -67,11 +67,15 @@ public class FeatureFlagsManager implements Managed { refreshFeatureFlags(); } + public Map getAllFlags() { + return featureFlags.get(); + } + @VisibleForTesting void refreshFeatureFlags() { final Map refreshedFeatureFlags = featureFlagDatabase.getFeatureFlags(); - featureFlags.set(refreshedFeatureFlags); + featureFlags.set(Collections.unmodifiableMap(refreshedFeatureFlags)); for (final Map.Entry entry : refreshedFeatureFlags.entrySet()) { final String featureFlag = entry.getKey(); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsControllerTest.java index 7bf0f365a..f1e99644f 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/FeatureFlagsControllerTest.java @@ -20,12 +20,15 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; import java.util.List; +import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; @RunWith(JUnitParamsRunner.class) public class FeatureFlagsControllerTest { @@ -83,6 +86,35 @@ public class FeatureFlagsControllerTest { } } + @SuppressWarnings("rawtypes") + @Test + public void testGet() { + final Map managedFlags = Map.of("activeFlag", true, "inactiveFlag", false); + when(FEATURE_FLAG_MANAGER.getAllFlags()).thenReturn(managedFlags); + + { + final Map returnedFlags = resources.getJerseyTest() + .target("/v1/featureflag") + .request() + .header("Token", "first") + .get(Map.class); + + verify(FEATURE_FLAG_MANAGER).getAllFlags(); + assertEquals(managedFlags, returnedFlags); + } + + { + final Response response = resources.getJerseyTest() + .target("/v1/featureflag") + .request() + .header("Token", "bogus-token") + .get(); + + assertEquals(401, response.getStatus()); + verifyNoMoreInteractions(FEATURE_FLAG_MANAGER); + } + } + @Test public void testDelete() { {