diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java index 9023eba9c..28f061f16 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java +++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java @@ -17,6 +17,7 @@ package org.whispersystems.textsecuregcm.controllers; import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.annotation.Timed; @@ -28,6 +29,7 @@ import org.whispersystems.textsecuregcm.entities.ClientContactTokens; import org.whispersystems.textsecuregcm.entities.ClientContacts; import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.storage.Account; +import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.util.Base64; import org.whispersystems.textsecuregcm.util.Constants; @@ -42,9 +44,12 @@ import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import java.io.IOException; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import static com.codahale.metrics.MetricRegistry.name; @@ -53,10 +58,33 @@ import io.dropwizard.auth.Auth; @Path("/v1/directory") public class DirectoryController { + private static final String[] FEEDBACK_STATUSES = { + "ok", + "mismatch", + "attestation-error", + "unexpected-error", + }; + private final Logger logger = LoggerFactory.getLogger(DirectoryController.class); private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); private final Histogram contactsHistogram = metricRegistry.histogram(name(getClass(), "contacts")); + private final Map iosFeedbackMeters = new HashMap() {{ + for (String status : FEEDBACK_STATUSES) { + put(status, metricRegistry.meter(name(DirectoryController.class, "feedback", "ios", status))); + } + }}; + private final Map androidFeedbackMeters = new HashMap() {{ + for (String status : FEEDBACK_STATUSES) { + put(status, metricRegistry.meter(name(DirectoryController.class, "feedback", "android", status))); + } + }}; + private final Map unknownFeedbackMeters = new HashMap() {{ + for (String status : FEEDBACK_STATUSES) { + put(status, metricRegistry.meter(name(DirectoryController.class, "feedback", "unknown", status))); + } + }}; + private final RateLimiters rateLimiters; private final DirectoryManager directory; private final DirectoryCredentialsGenerator userTokenGenerator; @@ -78,32 +106,29 @@ public class DirectoryController { return Response.ok().entity(userTokenGenerator.generateFor(account.getNumber())).build(); } - @Timed @PUT - @Path("/feedback/ok") - public Response setFeedbackOk(@Auth Account account) { - return Response.ok().build(); - } + @Path("/feedback/{status}") + public Response setFeedback(@Auth Account account, + @PathParam("status") String status) + { + Map platformFeedbackMeters = unknownFeedbackMeters; - @Timed - @PUT - @Path("/feedback/mismatch") - public Response setFeedbackMismatch(@Auth Account account) { - return Response.ok().build(); - } + Optional masterDevice = account.getMasterDevice(); + if (masterDevice.isPresent()) { + if (masterDevice.get().getApnId() != null) { + platformFeedbackMeters = iosFeedbackMeters; + } else if (masterDevice.get().getGcmId() != null) { + platformFeedbackMeters = androidFeedbackMeters; + } + } - @Timed - @PUT - @Path("/feedback/attestation-error") - public Response setFeedbackAttestationError(@Auth Account account) { - return Response.ok().build(); - } - - @Timed - @PUT - @Path("/feedback/unexpected-error") - public Response setFeedbackUnexpectedError(@Auth Account account) { - return Response.ok().build(); + Optional meter = Optional.ofNullable(platformFeedbackMeters.get(status)); + if (meter.isPresent()) { + meter.get().mark(); + return Response.ok().build(); + } else { + return Response.status(Status.NOT_FOUND).build(); + } } diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java index 553f7dc92..13e86c72f 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java @@ -20,6 +20,8 @@ import org.whispersystems.textsecuregcm.util.Base64; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.Response.Status.Family; import java.util.LinkedList; import java.util.List; @@ -66,6 +68,28 @@ public class DirectoryControllerTest { when(directoryCredentialsGenerator.generateFor(eq(AuthHelper.VALID_NUMBER))).thenReturn(validCredentials); } + @Test + public void testFeedbackOk() { + Response response = + resources.getJerseyTest() + .target("/v1/directory/feedback/ok") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .put(Entity.text("")); + assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.SUCCESSFUL); + } + + @Test + public void testNotFoundFeedback() { + Response response = + resources.getJerseyTest() + .target("/v1/directory/feedback/test-not-found") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .put(Entity.text("")); + assertThat(response.getStatusInfo()).isEqualTo(Status.NOT_FOUND); + } + @Test public void testGetAuthToken() { DirectoryCredentials token =