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;
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();
}
}
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)
@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";
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);
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);
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
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
*/
public static class TestResponse implements Response {
+ private TestStream stream;
+
public class TestStream implements Response.Stream {
private String mediaType;
private int status;
@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;
}
}
public Result assertNoContent() {
- //FIXME
- return this;
+ return assertStatus(HttpURLConnection.HTTP_NO_CONTENT);
}
public String outputAsString() {
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();