]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5334 Implement l10n cache control mechanism on server side
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 4 Jun 2014 09:51:43 +0000 (11:51 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 4 Jun 2014 13:49:02 +0000 (15:49 +0200)
sonar-server/src/main/java/org/sonar/server/platform/ws/L10nWs.java
sonar-server/src/test/java/org/sonar/server/platform/ws/L10nWsTest.java
sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QGatesWsTest.java
sonar-server/src/test/java/org/sonar/server/ws/WsTester.java

index f267b4acfc6a2fd1748a6fc7770ae22a51c0e7b3..5d75175186a38ff850cc7c478a20a9284cbb4c9d 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.platform.ws;
 
 import org.apache.commons.lang.LocaleUtils;
+import org.sonar.api.platform.Server;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
@@ -28,45 +29,61 @@ import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.server.user.UserSession;
 
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.Date;
 import java.util.Locale;
 
 public class L10nWs implements WebService {
 
   private final DefaultI18n i18n;
+  private final Server server;
 
-  public L10nWs(DefaultI18n i18n) {
+  public L10nWs(DefaultI18n i18n, Server server) {
     this.i18n = i18n;
+    this.server = server;
   }
 
   @Override
   public void define(Context context) {
     NewController l10n = context.createController("api/l10n");
     l10n.setDescription("Localization")
-      .setSince("4.4")
-      .createAction("index")
-        .setInternal(true)
-        .setDescription("Get all localization messages for a given locale")
-        .setHandler(new RequestHandler() {
-          @Override
-          public void handle(Request request, Response response) throws Exception {
-            serializeMessages(request, response);
-          }
-        }).createParam("locale")
-          .setDescription("BCP47 language tag, used to override the browser Accept-Language header")
-          .setExampleValue("fr-CH");
+      .setSince("4.4");
+    NewAction indexAction = l10n.createAction("index")
+      .setInternal(true)
+      .setDescription("Get all localization messages for a given locale")
+      .setHandler(new RequestHandler() {
+        @Override
+        public void handle(Request request, Response response) throws Exception {
+          serializeMessages(request, response);
+        }
+      });
+    indexAction.createParam("locale")
+      .setDescription("BCP47 language tag, used to override the browser Accept-Language header")
+      .setExampleValue("fr-CH");
+    indexAction.createParam("ts")
+    .setDescription("UTC timestamp of the last cache update")
+    .setExampleValue("2014-06-04T09:31:42Z");
+
     l10n.done();
   }
 
-  protected void serializeMessages(Request request, Response response) {
-    Locale locale = UserSession.get().locale();
-    String localeParam = request.param("locale");
-    if (localeParam != null) {
-      locale = LocaleUtils.toLocale(localeParam);
-    }
-    JsonWriter json = response.newJsonWriter().beginObject();
-    for (String messageKey: i18n.getPropertyKeys()) {
-      json.prop(messageKey, i18n.message(locale, messageKey, messageKey));
+  protected void serializeMessages(Request request, Response response) throws IOException {
+    Date timestamp = request.paramAsDateTime("ts");
+    if (timestamp != null && timestamp.after(server.getStartedAt())) {
+      response.stream().setStatus(HttpURLConnection.HTTP_NOT_MODIFIED).output().close();
+    } else {
+
+      Locale locale = UserSession.get().locale();
+      String localeParam = request.param("locale");
+      if (localeParam != null) {
+        locale = LocaleUtils.toLocale(localeParam);
+      }
+      JsonWriter json = response.newJsonWriter().beginObject();
+      for (String messageKey: i18n.getPropertyKeys()) {
+        json.prop(messageKey, i18n.message(locale, messageKey, messageKey));
+      }
+      json.endObject().close();
     }
-    json.endObject().close();
   }
 }
index 219405875f21b3979f19a6e4695096e6c22243ba..810f325b82724f60ffdc5535696076cc5f74e6c0 100644 (file)
@@ -24,14 +24,18 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.platform.Server;
+import org.sonar.api.utils.DateUtils;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.server.user.MockUserSession;
 import org.sonar.server.ws.WsTester;
 import org.sonar.server.ws.WsTester.Result;
 
+import java.util.Date;
 import java.util.Locale;
 
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -40,11 +44,34 @@ public class L10nWsTest {
   @Mock
   DefaultI18n i18n;
 
+  @Mock
+  Server server;
+
   @Test
-  public void should_return_all_l10n_messages_using_accept_header() throws Exception {
+  public void should_allow_client_to_cache_messages() throws Exception {
     Locale locale = Locale.PRC;
     MockUserSession.set().setLocale(locale);
 
+    Date now = new Date();
+    Date aBitLater = new Date(now.getTime() + 1000);
+    when(server.getStartedAt()).thenReturn(now);
+
+    Result result = new WsTester(new L10nWs(i18n, server)).newGetRequest("api/l10n", "index").setParam("ts", DateUtils.formatDateTime(aBitLater)).execute();
+    verifyZeroInteractions(i18n);
+    verify(server).getStartedAt();
+
+    result.assertNotModified();
+  }
+
+  @Test
+  public void should_return_all_l10n_messages_using_accept_header_with_cache_expired() throws Exception {
+    Locale locale = Locale.PRC;
+    MockUserSession.set().setLocale(locale);
+
+    Date now = new Date();
+    Date aBitEarlier = new Date(now.getTime() - 1000);
+    when(server.getStartedAt()).thenReturn(now);
+
     String key1 = "key1";
     String key2 = "key2";
     String key3 = "key3";
@@ -54,7 +81,7 @@ public class L10nWsTest {
     when(i18n.message(locale, key2, key2)).thenReturn(key2);
     when(i18n.message(locale, key3, key3)).thenReturn(key3);
 
-    Result result = new WsTester(new L10nWs(i18n)).newGetRequest("api/l10n", "index").execute();
+    Result result = new WsTester(new L10nWs(i18n, server)).newGetRequest("api/l10n", "index").setParam("ts", DateUtils.formatDateTime(aBitEarlier)).execute();
     verify(i18n).getPropertyKeys();
     verify(i18n).message(locale, key1, key1);
     verify(i18n).message(locale, key2, key2);
@@ -78,7 +105,7 @@ public class L10nWsTest {
     when(i18n.message(override, key2, key2)).thenReturn(key2);
     when(i18n.message(override, key3, key3)).thenReturn(key3);
 
-    Result result = new WsTester(new L10nWs(i18n)).newGetRequest("api/l10n", "index").setParam("locale", override.toString()).execute();
+    Result result = new WsTester(new L10nWs(i18n, server)).newGetRequest("api/l10n", "index").setParam("locale", override.toString()).execute();
     verify(i18n).getPropertyKeys();
     verify(i18n).message(override, key1, key1);
     verify(i18n).message(override, key2, key2);
index e57f5ebfaedf62012a83e9e54236da9434bb08c6..78adc0d479edabdce33a241936f73fc2ba5c25b4 100644 (file)
@@ -217,8 +217,8 @@ public class QGatesWsTest {
     Long id = 42L;
     String name = "New QG";
     when(qGates.rename(id, name)).thenReturn(new QualityGateDto().setId(id).setName(name));
-    tester.newGetRequest("api/qualitygates", "rename").setParam("id", id.toString()).setParam("name", name).execute()
-      .assertNoContent();
+    tester.newPostRequest("api/qualitygates", "rename").setParam("id", id.toString()).setParam("name", name).execute()
+      .assertJson("{'id':42,'name':'New QG'}");;
   }
 
   @Test
index 707312341528d95cc394aa057e5f243ce3c535f2..bae37674313a2c7fc1a0d78604b00b3a9acac4d6 100644 (file)
@@ -28,15 +28,20 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.internal.ValidatingRequest;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.utils.text.XmlWriter;
+import org.sonar.server.ws.WsTester.TestResponse.TestStream;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+
 import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.Map;
 
+import static org.fest.assertions.Assertions.assertThat;
+
 /**
  * @since 4.2
  */
@@ -82,6 +87,8 @@ public class WsTester {
 
   public static class TestResponse implements Response {
 
+    private TestStream stream;
+
     public class TestStream implements Response.Stream {
       private String mediaType;
       private int status;
@@ -127,12 +134,16 @@ public class WsTester {
 
     @Override
     public Stream stream() {
-      return new TestStream();
+      if (stream == null) {
+        stream = new TestStream();
+      }
+      return stream;
     }
 
 
     @Override
     public Response noContent() {
+      stream().setStatus(HttpURLConnection.HTTP_NO_CONTENT);
       IOUtils.closeQuietly(output);
       return this;
     }
@@ -147,8 +158,7 @@ public class WsTester {
     }
 
     public Result assertNoContent() {
-      //FIXME
-      return this;
+      return assertStatus(HttpURLConnection.HTTP_NO_CONTENT);
     }
 
     public String outputAsString() {
@@ -183,6 +193,16 @@ public class WsTester {
       JSONAssert.assertEquals(IOUtils.toString(url), json, strict);
       return this;
     }
+
+    public Result assertNotModified() {
+      return assertStatus(HttpURLConnection.HTTP_NOT_MODIFIED);
+    }
+
+    public Result assertStatus(int httpStatus) {
+      assertThat(((TestStream) response.stream()).status()).isEqualTo(httpStatus);
+      return this;
+    }
+
   }
 
   private final WebService.Context context = new WebService.Context();