aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2014-01-20 23:23:34 +0100
committerSimon Brandhof <simon.brandhof@gmail.com>2014-01-20 23:23:34 +0100
commit7371f8580263541c0012c800b79ab45d39aca739 (patch)
tree8c8228d1ce703905730701b31412f46f8a3281c4
parent6f26b4e5265c3f46343a90e0c7c722d32637c9c0 (diff)
downloadsonarqube-7371f8580263541c0012c800b79ab45d39aca739.tar.gz
sonarqube-7371f8580263541c0012c800b79ab45d39aca739.zip
SONAR-5010 refactor WebService framework
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java6
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java3
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java1
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java (renamed from sonar-server/src/main/java/org/sonar/server/issue/IssueFilterParameters.java)2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterResult.java (renamed from sonar-server/src/main/java/org/sonar/server/issue/IssueFilterResult.java)2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java (renamed from sonar-server/src/main/java/org/sonar/server/issue/IssueFilterService.java)23
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWs.java101
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/filter/package-info.java23
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/Platform.java9
-rw-r--r--sonar-server/src/main/java/org/sonar/server/rule/RuleWs.java (renamed from sonar-server/src/main/java/org/sonar/server/rule/RuleWebService.java)2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ws/ListingWs.java (renamed from sonar-server/src/main/java/org/sonar/server/ws/ListingWebService.java)2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java39
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java40
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb36
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java1
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java (renamed from sonar-server/src/test/java/org/sonar/server/issue/IssueFilterServiceTest.java)50
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWsTest.java96
-rw-r--r--sonar-server/src/test/java/org/sonar/server/rule/RuleWsTest.java (renamed from sonar-server/src/test/java/org/sonar/server/rule/RuleWebServiceTest.java)27
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ws/ListingWsTest.java (renamed from sonar-server/src/test/java/org/sonar/server/ws/ListingWebServiceTest.java)16
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java23
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ws/WsTester.java93
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json4
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json5
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json14
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json12
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/rule/RuleWsTest/search.json (renamed from sonar-server/src/test/resources/org/sonar/server/rule/RuleWebServiceTest/search.json)0
28 files changed, 478 insertions, 156 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java
index 59209cbee40..fb22807ed67 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java
@@ -40,6 +40,6 @@ public interface Response {
XmlWriter newXmlWriter();
- OutputStream output();
+ OutputStream stream();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java
index 9840b8abdcb..e24def28ec3 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleRequest.java
@@ -38,8 +38,10 @@ public class SimpleRequest extends Request {
return this;
}
- public SimpleRequest setParams(String key, String value) {
- this.params.put(key, value);
+ public SimpleRequest setParam(String key, @CheckForNull String value) {
+ if (value != null) {
+ params.put(key, value);
+ }
return this;
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java
index c6ae01d17b9..d2de13b9f63 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/SimpleResponse.java
@@ -42,7 +42,7 @@ public class SimpleResponse implements Response {
}
@Override
- public OutputStream output() {
+ public OutputStream stream() {
return output;
}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
index cf7a8b6320f..3228d3c0475 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
@@ -45,6 +45,9 @@ import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;
import org.sonar.core.resource.ResourceQuery;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.issue.filter.IssueFilterParameters;
+import org.sonar.server.issue.filter.IssueFilterResult;
+import org.sonar.server.issue.filter.IssueFilterService;
import org.sonar.server.user.UserSession;
import org.sonar.server.util.RubyUtils;
import org.sonar.server.util.Validation;
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
index 768ac484879..f288bbe227c 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
@@ -30,6 +30,7 @@ import org.sonar.api.issue.IssueQueryResult;
import org.sonar.api.issue.RubyIssueService;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.web.UserRole;
+import org.sonar.server.issue.filter.IssueFilterParameters;
import org.sonar.server.util.RubyUtils;
import javax.annotation.Nullable;
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueFilterParameters.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java
index 12ec6819dad..a3078f75e36 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/IssueFilterParameters.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.issue;
+package org.sonar.server.issue.filter;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueFilterResult.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterResult.java
index d1769f80350..90c0a465d30 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/IssueFilterResult.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterResult.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.issue;
+package org.sonar.server.issue.filter;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.issue.IssueQueryResult;
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueFilterService.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java
index 56d3e9c4227..1bff87a3378 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/IssueFilterService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.issue;
+package org.sonar.server.issue.filter;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
@@ -43,15 +43,11 @@ import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
-
import java.util.List;
import java.util.Map;
import static com.google.common.collect.Lists.newArrayList;
-/**
- * @since 3.7
- */
public class IssueFilterService implements ServerComponent {
private final IssueFilterDao filterDao;
@@ -60,8 +56,9 @@ public class IssueFilterService implements ServerComponent {
private final AuthorizationDao authorizationDao;
private final IssueFilterSerializer serializer;
- public IssueFilterService(IssueFilterDao filterDao, IssueFilterFavouriteDao favouriteDao, IssueFinder finder, AuthorizationDao authorizationDao,
- IssueFilterSerializer serializer) {
+ public IssueFilterService(IssueFilterDao filterDao, IssueFilterFavouriteDao favouriteDao,
+ IssueFinder finder, AuthorizationDao authorizationDao,
+ IssueFilterSerializer serializer) {
this.filterDao = filterDao;
this.favouriteDao = favouriteDao;
this.finder = finder;
@@ -205,7 +202,7 @@ public class IssueFilterService implements ServerComponent {
return issueFilterDto;
}
- public boolean canShareFilter(UserSession userSession){
+ public boolean canShareFilter(UserSession userSession) {
if (userSession.isLoggedIn()) {
String user = userSession.login();
return hasUserSharingPermission(user);
@@ -213,7 +210,7 @@ public class IssueFilterService implements ServerComponent {
return false;
}
- String getLoggedLogin(UserSession userSession) {
+ public String getLoggedLogin(UserSession userSession) {
String user = userSession.login();
if (!userSession.isLoggedIn()) {
throw new UnauthorizedException("User is not logged in");
@@ -221,7 +218,7 @@ public class IssueFilterService implements ServerComponent {
return user;
}
- void verifyCurrentUserCanReadFilter(DefaultIssueFilter issueFilter, String login) {
+ public void verifyCurrentUserCanReadFilter(DefaultIssueFilter issueFilter, String login) {
if (!issueFilter.user().equals(login) && !issueFilter.shared()) {
throw new ForbiddenException("User is not authorized to read this filter");
}
@@ -301,8 +298,8 @@ public class IssueFilterService implements ServerComponent {
private void addFavouriteIssueFilter(Long issueFilterId, String user) {
IssueFilterFavouriteDto issueFilterFavouriteDto = new IssueFilterFavouriteDto()
- .setIssueFilterId(issueFilterId)
- .setUserLogin(user);
+ .setIssueFilterId(issueFilterId)
+ .setUserLogin(user);
favouriteDao.insert(issueFilterFavouriteDto);
}
@@ -338,7 +335,7 @@ public class IssueFilterService implements ServerComponent {
return new IssueFilterResult(issueQueryResult, issueQuery);
}
- private boolean hasUserSharingPermission(String user){
+ private boolean hasUserSharingPermission(String user) {
return authorizationDao.selectGlobalPermissions(user).contains(GlobalPermissions.DASHBOARD_SHARING);
}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWs.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWs.java
new file mode 100644
index 00000000000..4326cc4319b
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWs.java
@@ -0,0 +1,101 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.issue.filter;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.UserSession;
+
+import java.util.List;
+
+public class IssueFilterWs implements WebService {
+
+ private final IssueFilterService service;
+
+ public IssueFilterWs(IssueFilterService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.newController("api/issue_filters")
+ .setSince("4.2")
+ .setDescription("Issue Filters");
+
+ NewAction search = controller.newAction("page");
+ search
+ .setDescription("Data required for rendering page 'Issues'. Internal use only.")
+ .setPrivate(true)
+ .setHandler(new RequestHandler() {
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ page(request, response);
+ }
+ });
+
+ controller.done();
+ }
+
+ private void page(Request request, Response response) {
+ UserSession session = UserSession.get();
+
+ JsonWriter json = response.newJsonWriter();
+ json.beginObject();
+
+ // Permissions
+ json.prop("canManageFilter", session.isLoggedIn());
+ json.prop("canBulkChange", session.isLoggedIn());
+
+ // Current filter (optional)
+ int filterId = request.intParam("id", -1);
+ if (filterId >= 0) {
+ DefaultIssueFilter filter = service.find((long)filterId, session);
+ json.name("filter")
+ .beginObject()
+ .prop("id", filter.id())
+ .prop("name", filter.name())
+ .prop("user", filter.user())
+ .prop("shared", filter.shared())
+ .prop("query", filter.data())
+ .endObject();
+ }
+
+ // Favorite filters, if logged in
+ if (session.isLoggedIn()) {
+ List<DefaultIssueFilter> favorites = service.findFavoriteFilters(session);
+ json.name("favorites").beginArray();
+ for (DefaultIssueFilter favorite : favorites) {
+ json
+ .beginObject()
+ .prop("id", favorite.id())
+ .prop("name", favorite.name())
+ .endObject();
+ }
+ json.endArray();
+ }
+
+ json.endObject();
+ json.close();
+ }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/filter/package-info.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/package-info.java
new file mode 100644
index 00000000000..2d8d8cbd9ea
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/issue/filter/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.issue.filter;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
index 85eca5cf65b..bb1ed4c77e9 100644
--- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
+++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
@@ -85,6 +85,8 @@ import org.sonar.server.es.ESNode;
import org.sonar.server.group.GroupMembershipFinder;
import org.sonar.server.group.InternalGroupMembershipService;
import org.sonar.server.issue.*;
+import org.sonar.server.issue.filter.IssueFilterService;
+import org.sonar.server.issue.filter.IssueFilterWs;
import org.sonar.server.notifications.NotificationCenter;
import org.sonar.server.notifications.NotificationService;
import org.sonar.server.permission.InternalPermissionService;
@@ -102,7 +104,7 @@ import org.sonar.server.ui.*;
import org.sonar.server.user.DefaultUserService;
import org.sonar.server.user.NewUserNotifier;
import org.sonar.server.util.*;
-import org.sonar.server.ws.ListingWebService;
+import org.sonar.server.ws.ListingWs;
import org.sonar.server.ws.WebServiceEngine;
import javax.annotation.Nullable;
@@ -268,7 +270,7 @@ public final class Platform {
// web services
servicesContainer.addSingleton(WebServiceEngine.class);
- servicesContainer.addSingleton(ListingWebService.class);
+ servicesContainer.addSingleton(ListingWs.class);
// quality profiles
servicesContainer.addSingleton(ProfileRules.class);
@@ -322,6 +324,7 @@ public final class Platform {
servicesContainer.addSingleton(IssueFilterService.class);
servicesContainer.addSingleton(IssueBulkChangeService.class);
servicesContainer.addSingleton(IssueChangelogFormatter.class);
+ servicesContainer.addSingleton(IssueFilterWs.class);
// issues actions
servicesContainer.addSingleton(AssignAction.class);
servicesContainer.addSingleton(PlanAction.class);
@@ -335,7 +338,7 @@ public final class Platform {
servicesContainer.addSingleton(RuleTagLookup.class);
servicesContainer.addSingleton(RubyRuleService.class);
servicesContainer.addSingleton(RuleRepositories.class);
- servicesContainer.addSingleton(RuleWebService.class);
+ servicesContainer.addSingleton(RuleWs.class);
// technical debt
servicesContainer.addSingleton(InternalRubyTechnicalDebtService.class);
diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleWebService.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleWs.java
index de35e668469..898cb4807a2 100644
--- a/sonar-server/src/main/java/org/sonar/server/rule/RuleWebService.java
+++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleWs.java
@@ -24,7 +24,7 @@ import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-public class RuleWebService implements WebService {
+public class RuleWs implements WebService {
@Override
public void define(Context context) {
diff --git a/sonar-server/src/main/java/org/sonar/server/ws/ListingWebService.java b/sonar-server/src/main/java/org/sonar/server/ws/ListingWs.java
index b1ffa0a1bd7..54c55f874f1 100644
--- a/sonar-server/src/main/java/org/sonar/server/ws/ListingWebService.java
+++ b/sonar-server/src/main/java/org/sonar/server/ws/ListingWs.java
@@ -33,7 +33,7 @@ import java.util.List;
*
* @since 4.2
*/
-public class ListingWebService implements WebService {
+public class ListingWs implements WebService {
@Override
public void define(final Context context) {
diff --git a/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java b/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java
index 448bf213b6f..d5b001b2f57 100644
--- a/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java
+++ b/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java
@@ -19,13 +19,18 @@
*/
package org.sonar.server.ws;
+import org.apache.commons.io.Charsets;
+import org.apache.commons.io.IOUtils;
+import org.sonar.api.server.ws.Response;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.utils.text.XmlWriter;
-import org.sonar.api.server.ws.Response;
+import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.StringWriter;
public class ServletResponse implements Response {
@@ -37,11 +42,7 @@ public class ServletResponse implements Response {
@Override
public JsonWriter newJsonWriter() {
- try {
- return JsonWriter.of(source.getWriter());
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
+ return JsonWriter.of(new Buffer(source));
}
@Override
@@ -54,7 +55,7 @@ public class ServletResponse implements Response {
}
@Override
- public OutputStream output() {
+ public OutputStream stream() {
try {
return source.getOutputStream();
} catch (IOException e) {
@@ -72,4 +73,28 @@ public class ServletResponse implements Response {
source.setStatus(httpStatus);
return this;
}
+
+ private static class Buffer extends StringWriter {
+ private final HttpServletResponse httpResponse;
+
+ public Buffer(HttpServletResponse httpResponse) {
+ this.httpResponse = httpResponse;
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+
+ ServletOutputStream stream = null;
+ try {
+ stream = httpResponse.getOutputStream();
+ IOUtils.copy(new ByteArrayInputStream(toString().getBytes(Charsets.UTF_8)), stream);
+ stream.flush();
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to flush buffer", e);
+ } finally {
+ IOUtils.closeQuietly(stream);
+ }
+ }
+ }
}
diff --git a/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java b/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java
index b1556e57094..43f10176456 100644
--- a/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java
+++ b/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java
@@ -21,12 +21,15 @@ package org.sonar.server.ws;
import org.picocontainer.Startable;
import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ServerException;
import javax.servlet.http.HttpServletResponse;
+import java.io.OutputStreamWriter;
import java.util.List;
/**
@@ -34,15 +37,6 @@ import java.util.List;
*/
public class WebServiceEngine implements ServerComponent, Startable {
- static class RequestException extends Exception {
- private final int httpStatus;
-
- RequestException(int httpStatus, String message) {
- super(message);
- this.httpStatus = httpStatus;
- }
- }
-
private final WebService.Context context;
public WebServiceEngine(WebService[] webServices) {
@@ -52,10 +46,6 @@ public class WebServiceEngine implements ServerComponent, Startable {
}
}
- public WebServiceEngine() {
- this(new WebService[0]);
- }
-
@Override
public void start() {
// Force execution of constructor to be sure that web services
@@ -75,36 +65,36 @@ public class WebServiceEngine implements ServerComponent, Startable {
}
public void execute(Request request, Response response,
- String controllerPath, String actionKey) {
+ String controllerPath, String actionKey) {
try {
WebService.Action action = getAction(controllerPath, actionKey);
verifyRequest(action, request);
action.handler().handle(request, response);
- } catch (RequestException e) {
- sendError(e.httpStatus, e.getMessage(), response);
+ } catch (ServerException e) {
+ // TODO support ServerException l10n messages
+ sendError(e.httpCode(), e.getMessage(), response);
} catch (Exception e) {
- // TODO support authentication exceptions and others...
sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), response);
}
}
- private WebService.Action getAction(String controllerPath, String actionKey) throws RequestException {
+ private WebService.Action getAction(String controllerPath, String actionKey) {
WebService.Controller controller = context.controller(controllerPath);
if (controller == null) {
- throw new RequestException(HttpServletResponse.SC_BAD_REQUEST, String.format("Unknown web service: %s", controllerPath));
+ throw new BadRequestException(String.format("Unknown web service: %s", controllerPath));
}
WebService.Action action = controller.action(actionKey);
if (action == null) {
- throw new RequestException(HttpServletResponse.SC_BAD_REQUEST, String.format("Unknown action: %s/%s", controllerPath, actionKey));
+ throw new BadRequestException(String.format("Unknown action: %s/%s", controllerPath, actionKey));
}
return action;
}
- private void verifyRequest(WebService.Action action, Request request) throws RequestException {
+ private void verifyRequest(WebService.Action action, Request request) {
if (request.isPost() != action.isPost()) {
- throw new RequestException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Method POST is required");
+ throw new ServerException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Method POST is required");
}
// TODO verify required parameters
}
@@ -112,7 +102,9 @@ public class WebServiceEngine implements ServerComponent, Startable {
private void sendError(int status, String message, Response response) {
response.setStatus(status);
- JsonWriter json = response.newJsonWriter();
+ // Reset response by directly using the stream. Response#newJsonWriter()
+ // must not be used because it potentially contains some partial response
+ JsonWriter json = JsonWriter.of(new OutputStreamWriter(response.stream()));
json.beginObject();
json.name("errors").beginArray();
json.beginObject().prop("msg", message).endObject();
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
index 99c91d1c19d..57485a69e83 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
@@ -60,42 +60,6 @@ class IssuesController < ApplicationController
end
end
- # GET /issues/init?[id=<optional filter id>]
- def page_init
- hash = {}
- hash[:canBulkChange]=logged_in?
- hash[:canManageFilter]=logged_in?
-
- if logged_in?
- favorite_filters = Internal.issues.findFavouriteIssueFiltersForCurrentUser()
- hash[:favorites] = favorite_filters.map do |filter|
- {
- :id => filter.id().to_i,
- :name => filter.name(),
- :user => filter.user(),
- :shared => filter.shared()
- # no need to export description and query fields
- }
- end
- end
-
- if params[:id]
- filter = Internal.issues.findIssueFilter(params[:id].to_i)
- hash[:filter] = {
- :id => filter.id().to_i,
- :name => filter.name(),
- :user => filter.user(),
- :shared => filter.shared(),
- :description => filter.description(),
- :query => filter.data()
- }
- end
-
- respond_to do |format|
- format.json { render :json => hash, :status => 200 }
- end
- end
-
# Load existing filter
# GET /issues/filter/<filter id>
def filter
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
index 850d665e4b3..534283db21a 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
@@ -36,6 +36,7 @@ import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;
import org.sonar.core.resource.ResourceQuery;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.issue.filter.IssueFilterService;
import org.sonar.server.user.UserSession;
import java.util.Collections;
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueFilterServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java
index c915404efe9..6bfd5b873c9 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/IssueFilterServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java
@@ -18,13 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.issue;
+package org.sonar.server.issue.filter;
import com.google.common.collect.Lists;
import org.apache.commons.lang.ObjectUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
-import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.api.issue.IssueFinder;
@@ -62,33 +61,18 @@ import static org.mockito.Mockito.*;
public class IssueFilterServiceTest {
- private IssueFilterService service;
-
- private IssueFilterDao issueFilterDao;
- private IssueFilterFavouriteDao issueFilterFavouriteDao;
- private IssueFinder issueFinder;
- private AuthorizationDao authorizationDao;
- private IssueFilterSerializer issueFilterSerializer;
-
- private UserSession userSession;
-
- @Before
- public void before() {
- userSession = MockUserSession.create().setLogin("john");
-
- issueFilterDao = mock(IssueFilterDao.class);
- issueFilterFavouriteDao = mock(IssueFilterFavouriteDao.class);
- issueFinder = mock(IssueFinder.class);
- authorizationDao = mock(AuthorizationDao.class);
- issueFilterSerializer = mock(IssueFilterSerializer.class);
-
- service = new IssueFilterService(issueFilterDao, issueFilterFavouriteDao, issueFinder, authorizationDao, issueFilterSerializer);
- }
+ IssueFilterDao issueFilterDao = mock(IssueFilterDao.class);
+ IssueFilterFavouriteDao issueFilterFavouriteDao = mock(IssueFilterFavouriteDao.class);
+ IssueFinder issueFinder = mock(IssueFinder.class);
+ AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+ IssueFilterSerializer issueFilterSerializer = mock(IssueFilterSerializer.class);
+ UserSession userSession = MockUserSession.create().setLogin("john");
+ IssueFilterService service = new IssueFilterService(issueFilterDao, issueFilterFavouriteDao, issueFinder, authorizationDao, issueFilterSerializer);
@Test
public void should_find_by_id() {
- IssueFilterDto issueFilterDto = new IssueFilterDto().setId(1L).setName("My Issue").setUserLogin("john");
- when(issueFilterDao.selectById(1L)).thenReturn(issueFilterDto);
+ IssueFilterDto dto = new IssueFilterDto().setId(1L).setName("My Issue").setUserLogin("john");
+ when(issueFilterDao.selectById(1L)).thenReturn(dto);
DefaultIssueFilter issueFilter = service.findById(1L);
assertThat(issueFilter).isNotNull();
@@ -218,7 +202,7 @@ public class IssueFilterServiceTest {
@Test
public void should_not_save_shared_filter_if_name_already_used_by_shared_filter() {
- when(issueFilterDao.selectByUser(eq("john"))).thenReturn(Collections.<IssueFilterDto> emptyList());
+ when(issueFilterDao.selectByUser(eq("john"))).thenReturn(Collections.<IssueFilterDto>emptyList());
when(issueFilterDao.selectSharedFilters()).thenReturn(newArrayList(new IssueFilterDto().setId(1L).setName("My Issue").setUserLogin("henry").setShared(true)));
DefaultIssueFilter issueFilter = new DefaultIssueFilter().setName("My Issue").setShared(true);
try {
@@ -546,9 +530,9 @@ public class IssueFilterServiceTest {
@Test
public void should_find_shared_issue_filter() {
when(issueFilterDao.selectSharedFilters()).thenReturn(newArrayList(
- new IssueFilterDto().setId(1L).setName("My Issue").setUserLogin("john").setShared(true),
- new IssueFilterDto().setId(2L).setName("Project Issues").setUserLogin("arthur").setShared(true)
- ));
+ new IssueFilterDto().setId(1L).setName("My Issue").setUserLogin("john").setShared(true),
+ new IssueFilterDto().setId(2L).setName("Project Issues").setUserLogin("arthur").setShared(true)
+ ));
List<DefaultIssueFilter> results = service.findSharedFiltersWithoutUserFilters(userSession);
assertThat(results).hasSize(1);
@@ -580,7 +564,7 @@ public class IssueFilterServiceTest {
public void should_add_favourite_issue_filter_id() {
when(issueFilterDao.selectById(1L)).thenReturn(new IssueFilterDto().setId(1L).setName("My Issues").setUserLogin("john").setData("componentRoots=struts"));
// The filter is not in the favorite list --> add to favorite
- when(issueFilterFavouriteDao.selectByFilterId(1L)).thenReturn(Collections.<IssueFilterFavouriteDto> emptyList());
+ when(issueFilterFavouriteDao.selectByFilterId(1L)).thenReturn(Collections.<IssueFilterFavouriteDto>emptyList());
ArgumentCaptor<IssueFilterFavouriteDto> issueFilterFavouriteDtoCaptor = ArgumentCaptor.forClass(IssueFilterFavouriteDto.class);
boolean result = service.toggleFavouriteIssueFilter(1L, userSession);
@@ -596,7 +580,7 @@ public class IssueFilterServiceTest {
public void should_add_favourite_on_shared_filter() {
when(issueFilterDao.selectById(1L)).thenReturn(new IssueFilterDto().setId(1L).setName("My Issues").setUserLogin("arthur").setShared(true));
// The filter is not in the favorite list --> add to favorite
- when(issueFilterFavouriteDao.selectByFilterId(1L)).thenReturn(Collections.<IssueFilterFavouriteDto> emptyList());
+ when(issueFilterFavouriteDao.selectByFilterId(1L)).thenReturn(Collections.<IssueFilterFavouriteDto>emptyList());
ArgumentCaptor<IssueFilterFavouriteDto> issueFilterFavouriteDtoCaptor = ArgumentCaptor.forClass(IssueFilterFavouriteDto.class);
boolean result = service.toggleFavouriteIssueFilter(1L, userSession);
@@ -653,7 +637,7 @@ public class IssueFilterServiceTest {
}
@Test
- public void user_can_share_filter_if_logged_and_own_sharing_permission(){
+ public void user_can_share_filter_if_logged_and_own_sharing_permission() {
when(authorizationDao.selectGlobalPermissions("john")).thenReturn(newArrayList(GlobalPermissions.DASHBOARD_SHARING));
UserSession userSession = MockUserSession.create().setLogin("john");
assertThat(service.canShareFilter(userSession)).isTrue();
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWsTest.java b/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWsTest.java
new file mode 100644
index 00000000000..250a1009a73
--- /dev/null
+++ b/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWsTest.java
@@ -0,0 +1,96 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.issue.filter;
+
+import org.junit.Test;
+import org.sonar.api.server.ws.SimpleRequest;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class IssueFilterWsTest {
+
+ IssueFilterService service = mock(IssueFilterService.class);
+ IssueFilterWs ws = new IssueFilterWs(service);
+ WsTester tester = new WsTester(ws);
+
+ @Test
+ public void define_ws() throws Exception {
+ WebService.Controller controller = tester.controller("api/issue_filters");
+ assertThat(controller).isNotNull();
+ assertThat(controller.description()).isNotEmpty();
+ assertThat(controller.since()).isEqualTo("4.2");
+
+ WebService.Action index = controller.action("page");
+ assertThat(index).isNotNull();
+ assertThat(index.handler()).isNotNull();
+ assertThat(index.isPost()).isFalse();
+ assertThat(index.isPrivate()).isTrue();
+ }
+
+ @Test
+ public void anonymous_page() throws Exception {
+ MockUserSession.set().setLogin(null);
+ SimpleRequest request = new SimpleRequest();
+ tester.execute("page", request).assertJson(getClass(), "anonymous_page.json");
+ }
+
+ @Test
+ public void logged_in_page() throws Exception {
+ MockUserSession.set().setLogin("eric").setUserId(123);
+ SimpleRequest request = new SimpleRequest();
+ tester.execute("page", request)
+ .assertHttpStatus(200)
+ .assertJson(getClass(), "logged_in_page.json");
+ }
+
+ @Test
+ public void logged_in_page_with_favorites() throws Exception {
+ MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
+ when(service.findFavoriteFilters(session)).thenReturn(Arrays.asList(
+ new DefaultIssueFilter().setId(6L).setName("My issues"),
+ new DefaultIssueFilter().setId(13L).setName("Blocker issues")
+ ));
+ SimpleRequest request = new SimpleRequest();
+ tester.execute("page", request)
+ .assertHttpStatus(200)
+ .assertJson(getClass(), "logged_in_page_with_favorites.json");
+ }
+
+ @Test
+ public void logged_in_page_with_selected_filter() throws Exception {
+ MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
+ when(service.find(13L, session)).thenReturn(
+ new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER")
+ );
+
+ SimpleRequest request = new SimpleRequest().setParam("id", "13");
+ tester.execute("page", request)
+ .assertHttpStatus(200)
+ .assertJson(getClass(), "logged_in_page_with_selected_filter.json");
+ }
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleWebServiceTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleWsTest.java
index 69e9c4bdc5d..8778dfca58f 100644
--- a/sonar-server/src/test/java/org/sonar/server/rule/RuleWebServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleWsTest.java
@@ -19,26 +19,21 @@
*/
package org.sonar.server.rule;
-import org.apache.commons.io.IOUtils;
import org.junit.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.SimpleRequest;
-import org.sonar.api.server.ws.SimpleResponse;
import org.sonar.api.server.ws.WebService;
+import org.sonar.server.ws.WsTester;
import static org.fest.assertions.Assertions.assertThat;
-public class RuleWebServiceTest {
+public class RuleWsTest {
- WebService.Context context = new WebService.Context();
- RuleWebService ws = new RuleWebService();
+ RuleWs ws = new RuleWs();
+ WsTester tester = new WsTester(ws);
@Test
public void define_ws() throws Exception {
- ws.define(context);
-
- WebService.Controller controller = context.controller("api/rules");
+ WebService.Controller controller = tester.controller("api/rules");
assertThat(controller).isNotNull();
assertThat(controller.path()).isEqualTo("api/rules");
assertThat(controller.description()).isNotEmpty();
@@ -53,17 +48,7 @@ public class RuleWebServiceTest {
@Test
public void search_for_rules() throws Exception {
- ws.define(context);
- RequestHandler handler = context.controller("api/rules").action("search").handler();
SimpleRequest request = new SimpleRequest();
- SimpleResponse response = new SimpleResponse();
-
- handler.handle(request, response);
-
- String json = response.outputAsString();
- JSONAssert.assertEquals(
- IOUtils.toString(getClass().getResource("/org/sonar/server/rule/RuleWebServiceTest/search.json")),
- json, true
- );
+ tester.execute("search", request).assertJson(getClass(), "search.json");
}
}
diff --git a/sonar-server/src/test/java/org/sonar/server/ws/ListingWebServiceTest.java b/sonar-server/src/test/java/org/sonar/server/ws/ListingWsTest.java
index 41aebdbf0b7..ce1388bd33e 100644
--- a/sonar-server/src/test/java/org/sonar/server/ws/ListingWebServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/ws/ListingWsTest.java
@@ -26,13 +26,14 @@ import org.sonar.api.server.ws.*;
import static org.fest.assertions.Assertions.assertThat;
-public class ListingWebServiceTest {
+public class ListingWsTest {
+
+ ListingWs ws = new ListingWs();
+ WsTester tester = new WsTester(ws);
+
@Test
public void define_ws() throws Exception {
- WebService.Context context = new WebService.Context();
- new ListingWebService().define(context);
-
- WebService.Controller controller = context.controller("api/webservices");
+ WebService.Controller controller = tester.controller("api/webservices");
assertThat(controller).isNotNull();
assertThat(controller.path()).isEqualTo("api/webservices");
assertThat(controller.description()).isNotEmpty();
@@ -52,12 +53,11 @@ public class ListingWebServiceTest {
public void index() throws Exception {
// register web services, including itself
WebService.Context context = new WebService.Context();
- ListingWebService listingWs = new ListingWebService();
- listingWs.define(context);
+ ws.define(context);
new MetricWebService().define(context);
SimpleResponse response = new SimpleResponse();
- listingWs.list(context.controllers(), response);
+ ws.list(context.controllers(), response);
JSONAssert.assertEquals(
IOUtils.toString(getClass().getResource("/org/sonar/server/ws/ListingWebServiceTest/index.json")),
diff --git a/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java b/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java
index 727f07d2627..6893f691f58 100644
--- a/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java
@@ -82,8 +82,18 @@ public class WebServiceEngineTest {
SimpleResponse response = new SimpleResponse();
engine.execute(request, response, "api/system", "ping");
- assertThat(response.outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"Method POST is required\"}]}");
assertThat(response.status()).isEqualTo(405);
+ assertThat(response.outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"Method POST is required\"}]}");
+ }
+
+ @Test
+ public void internal_error() throws Exception {
+ Request request = new SimpleRequest();
+ SimpleResponse response = new SimpleResponse();
+ engine.execute(request, response, "api/system", "fail");
+
+ assertThat(response.status()).isEqualTo(500);
+ assertThat(response.outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"Unexpected\"}]}");
}
static class SystemWebService implements WebService {
@@ -94,7 +104,7 @@ public class WebServiceEngineTest {
.setHandler(new RequestHandler() {
@Override
public void handle(Request request, Response response) throws Exception {
- response.output().write("good".getBytes());
+ response.stream().write("good".getBytes());
}
});
newController.newAction("ping")
@@ -102,7 +112,14 @@ public class WebServiceEngineTest {
.setHandler(new RequestHandler() {
@Override
public void handle(Request request, Response response) throws Exception {
- response.output().write("pong".getBytes());
+ response.stream().write("pong".getBytes());
+ }
+ });
+ newController.newAction("fail")
+ .setHandler(new RequestHandler() {
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ throw new IllegalStateException("Unexpected");
}
});
newController.done();
diff --git a/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java b/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java
new file mode 100644
index 00000000000..54afb95604f
--- /dev/null
+++ b/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.ws;
+
+import org.apache.commons.io.IOUtils;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.SimpleResponse;
+import org.sonar.api.server.ws.WebService;
+
+import javax.annotation.CheckForNull;
+import java.net.URL;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * TODO move to sonar-plugin-api with type "test-jar"
+ */
+public class WsTester {
+
+ private final WebService.Context context = new WebService.Context();
+ private String wsPath = null;
+
+ public WsTester(WebService ws) {
+ ws.define(context);
+ if (!context.controllers().isEmpty()) {
+ wsPath = context.controllers().get(0).path();
+ }
+ }
+
+ public WebService.Context context() {
+ return context;
+ }
+
+ @CheckForNull
+ public WebService.Controller controller(String path) {
+ return context.controller(path);
+ }
+
+ public Result execute(String actionKey, Request request) throws Exception {
+ if (wsPath == null) {
+ throw new IllegalStateException("Ws path is not defined");
+ }
+ SimpleResponse response = new SimpleResponse();
+ RequestHandler handler = context.controller(wsPath).action(actionKey).handler();
+ handler.handle(request, response);
+ return new Result(response);
+ }
+
+ public static class Result {
+ private final SimpleResponse response;
+
+ private Result(SimpleResponse response) {
+ this.response = response;
+ }
+
+ public Result assertHttpStatus(int httpStatus) {
+ assertEquals(httpStatus, response.status());
+ return this;
+ }
+
+ public Result assertJson(String expectedJson) throws Exception {
+ String json = response.outputAsString();
+ JSONAssert.assertEquals(expectedJson, json, true);
+ return this;
+ }
+
+ public Result assertJson(Class clazz, String jsonResourcePath) throws Exception {
+ String json = response.outputAsString();
+ URL url = clazz.getResource(clazz.getSimpleName() + "/" + jsonResourcePath);
+ JSONAssert.assertEquals(IOUtils.toString(url), json, true);
+ return this;
+ }
+ }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json
new file mode 100644
index 00000000000..7b3ea5bfb13
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json
@@ -0,0 +1,4 @@
+{
+ "canManageFilter": false,
+ "canBulkChange": false
+} \ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json
new file mode 100644
index 00000000000..1db0d13a5f0
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json
@@ -0,0 +1,5 @@
+{
+ "canManageFilter": true,
+ "canBulkChange": true,
+ "favorites": []
+} \ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json
new file mode 100644
index 00000000000..4f4babeeea1
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json
@@ -0,0 +1,14 @@
+{
+ "canManageFilter": true,
+ "canBulkChange": true,
+ "favorites": [
+ {
+ "id": 6,
+ "name": "My issues"
+ },
+ {
+ "id": 13,
+ "name": "Blocker issues"
+ }
+ ]
+} \ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json
new file mode 100644
index 00000000000..18b60b0ccbe
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json
@@ -0,0 +1,12 @@
+{
+ "canManageFilter": true,
+ "canBulkChange": true,
+ "filter": {
+ "id": 13,
+ "name": "Blocker issues",
+ "shared": false,
+ "query": "severity=BLOCKER"
+ },
+ "favorites": [
+ ]
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleWebServiceTest/search.json b/sonar-server/src/test/resources/org/sonar/server/rule/RuleWsTest/search.json
index a3a00e8e344..a3a00e8e344 100644
--- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleWebServiceTest/search.json
+++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleWsTest/search.json