aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2014-05-28 12:43:13 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2014-05-28 12:43:13 +0200
commit19397ac0aaf022afabbbdde4686edc550c8a5d36 (patch)
tree751aac3c3a68655671b02bc210a474411bb2944a
parent52e6ce846f536ec245578525c9a200f81e649681 (diff)
downloadsonarqube-19397ac0aaf022afabbbdde4686edc550c8a5d36.tar.gz
sonarqube-19397ac0aaf022afabbbdde4686edc550c8a5d36.zip
SONAR-5341 In /issues/search WS, add parameter 'extra_fields'
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java20
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java9
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java90
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java420
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java66
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java88
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java6
-rw-r--r--sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json3
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java209
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java377
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java77
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java21
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json32
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json24
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json26
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json58
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json26
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json24
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json39
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json39
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json39
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json27
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json27
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json27
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json29
25 files changed, 1478 insertions, 325 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
index ee422a76070..f4c0a40d3f4 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
@@ -22,9 +22,11 @@ package org.sonar.api.server.ws;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.DateUtils;
import javax.annotation.CheckForNull;
+import java.util.Date;
import java.util.List;
/**
@@ -93,6 +95,7 @@ public abstract class Request {
return values;
}
+ @CheckForNull
public List<String> paramAsStrings(String key) {
String value = param(key);
if (value == null) {
@@ -182,9 +185,16 @@ public abstract class Request {
return result;
}
-// @CheckForNull
-// public Date paramAsDate(String key) {
-// String s = param(key);
-// return s == null ? null : Long.parseLong(s);
-// }
+ @CheckForNull
+ public Date paramAsDate(String key) {
+ String s = param(key);
+ if (s != null) {
+ Date date = DateUtils.parseDateTimeQuietly(s);
+ if (date != null) {
+ return date;
+ }
+ return DateUtils.parseDate(s);
+ }
+ return null;
+ }
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java
index 9d2926460f6..a4b34acd653 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java
@@ -24,8 +24,10 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.server.ws.internal.ValidatingRequest;
+import org.sonar.api.utils.DateUtils;
import javax.annotation.Nullable;
+
import java.util.Map;
import static org.fest.assertions.Assertions.assertThat;
@@ -71,6 +73,7 @@ public class RequestTest {
action.createParam("a_boolean");
action.createParam("a_number");
action.createParam("a_enum");
+ action.createParam("a_date");
action.createParam("a_required_string").setRequired(true);
action.createParam("a_required_boolean").setRequired(true);
@@ -178,6 +181,12 @@ public class RequestTest {
}
@Test
+ public void param_as_date() throws Exception {
+ assertThat(request.setParam("a_date", "2014-05-27").paramAsDate("a_date")).isEqualTo(DateUtils.parseDate("2014-05-27"));
+ assertThat(request.setParam("a_date", "2014-05-27T15:50:45+0100").paramAsDate("a_date")).isEqualTo(DateUtils.parseDateTime("2014-05-27T15:50:45+0100"));
+ }
+
+ @Test
public void param_as_strings() throws Exception {
assertThat(request.paramAsStrings("a_string")).isNull();
assertThat(request.setParam("a_string", "").paramAsStrings("a_string")).isEmpty();
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java
new file mode 100644
index 00000000000..e4d7cfa04da
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.ws;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.action.Action;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.workflow.Transition;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.user.UserSession;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class IssueActionsWriter implements ServerComponent {
+
+ private final IssueService issueService;
+ private final ActionService actionService;
+
+ public IssueActionsWriter(IssueService issueService, ActionService actionService) {
+ this.issueService = issueService;
+ this.actionService = actionService;
+ }
+
+ public void writeTransitions(Issue issue, JsonWriter json) {
+ json.name("transitions").beginArray();
+ if (UserSession.get().isLoggedIn()) {
+ List<Transition> transitions = issueService.listTransitions(issue, UserSession.get());
+ for (Transition transition : transitions) {
+ json.value(transition.key());
+ }
+ }
+ json.endArray();
+ }
+
+ public void writeActions(Issue issue, JsonWriter json) {
+ json.name("actions").beginArray();
+ for (String action : actions((DefaultIssue) issue)) {
+ json.value(action);
+ }
+ json.endArray();
+ }
+
+ private List<String> actions(DefaultIssue issue) {
+ List<String> actions = newArrayList();
+ String login = UserSession.get().login();
+ if (login != null) {
+ actions.add("comment");
+ if (issue.resolution() == null) {
+ actions.add("assign");
+ if (!login.equals(issue.assignee())) {
+ actions.add("assign_to_me");
+ }
+ actions.add("plan");
+ String projectKey = issue.projectKey();
+ if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) {
+ actions.add("set_severity");
+ }
+ for (Action action : actionService.listAvailableActions(issue)) {
+ actions.add(action.key());
+ }
+ }
+ }
+ return actions;
+ }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java
new file mode 100644
index 00000000000..36d98974739
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java
@@ -0,0 +1,420 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.ws;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Resources;
+import org.sonar.api.component.Component;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.issue.*;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.Rule;
+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.user.User;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.server.issue.filter.IssueFilterParameters;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class IssueSearchAction implements RequestHandler {
+
+ private static final String EXTRA_FIELDS_PARAM = "extra_fields";
+
+ private final IssueFinder issueFinder;
+ private final IssueActionsWriter actionsWriter;
+ private final I18n i18n;
+ private final Durations durations;
+
+ public IssueSearchAction(IssueFinder issueFinder, IssueActionsWriter actionsWriter, I18n i18n, Durations durations) {
+ this.issueFinder = issueFinder;
+ this.actionsWriter = actionsWriter;
+ this.i18n = i18n;
+ this.durations = durations;
+ }
+
+ void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("search")
+ .setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. " +
+ "Requires Browse permission on project(s)")
+ .setSince("3.6")
+ .setHandler(this)
+ .setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
+
+ action.createParam(IssueFilterParameters.ISSUES)
+ .setDescription("Comma-separated list of issue keys")
+ .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
+ action.createParam(IssueFilterParameters.SEVERITIES)
+ .setDescription("Comma-separated list of severities")
+ .setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
+ .setPossibleValues(Severity.ALL);
+ action.createParam(IssueFilterParameters.STATUSES)
+ .setDescription("Comma-separated list of statuses")
+ .setExampleValue(Issue.STATUS_OPEN + "," + Issue.STATUS_REOPENED)
+ .setPossibleValues(Issue.STATUSES);
+ action.createParam(IssueFilterParameters.RESOLUTIONS)
+ .setDescription("Comma-separated list of resolutions")
+ .setExampleValue(Issue.RESOLUTION_FIXED + "," + Issue.RESOLUTION_REMOVED)
+ .setPossibleValues(Issue.RESOLUTIONS);
+ action.createParam(IssueFilterParameters.RESOLVED)
+ .setDescription("To match resolved or unresolved issues")
+ .setBooleanPossibleValues();
+ action.createParam(IssueFilterParameters.COMPONENTS)
+ .setDescription("To retrieve issues associated to a specific list of components (comma-separated list of component keys). " +
+ "Note that if you set the value to a project key, only issues associated to this project are retrieved. " +
+ "Issues associated to its sub-components (such as files, packages, etc.) are not retrieved. See also componentRoots")
+ .setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
+ action.createParam(IssueFilterParameters.COMPONENT_ROOTS)
+ .setDescription("To retrieve issues associated to a specific list of components and their sub-components (comma-separated list of component keys). " +
+ "Views are not supported")
+ .setExampleValue("org.apache.struts:struts");
+ action.createParam(IssueFilterParameters.RULES)
+ .setDescription("Comma-separated list of coding rule keys. Format is <repository>:<rule>")
+ .setExampleValue("squid:AvoidCycles");
+ action.createParam(IssueFilterParameters.HIDE_RULES)
+ .setDescription("To not return rules")
+ .setBooleanPossibleValues();
+ action.createParam(IssueFilterParameters.ACTION_PLANS)
+ .setDescription("Comma-separated list of action plan keys (not names)")
+ .setExampleValue("3f19de90-1521-4482-a737-a311758ff513");
+ action.createParam(IssueFilterParameters.PLANNED)
+ .setDescription("To retrieve issues associated to an action plan or not")
+ .setBooleanPossibleValues();
+ action.createParam(IssueFilterParameters.REPORTERS)
+ .setDescription("Comma-separated list of reporter logins")
+ .setExampleValue("admin");
+ action.createParam(IssueFilterParameters.ASSIGNEES)
+ .setDescription("Comma-separated list of assignee logins")
+ .setExampleValue("admin,usera");
+ action.createParam(IssueFilterParameters.ASSIGNED)
+ .setDescription("To retrieve assigned or unassigned issues")
+ .setBooleanPossibleValues();
+ action.createParam(IssueFilterParameters.LANGUAGES)
+ .setDescription("Comma-separated list of languages. Available since 4.4")
+ .setExampleValue("java,js");
+ action.createParam(EXTRA_FIELDS_PARAM)
+ .setDescription("Add some extra fields on each issue. Available since 4.4")
+ .setPossibleValues("actions", "transitions", "assigneeName", "actionPlanName");
+ action.createParam(IssueFilterParameters.CREATED_AT)
+ .setDescription("To retrieve issues created at a given date. Format: date or datetime ISO formats")
+ .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
+ action.createParam(IssueFilterParameters.CREATED_AFTER)
+ .setDescription("To retrieve issues created after the given date (inclusive). Format: date or datetime ISO formats")
+ .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
+ action.createParam(IssueFilterParameters.CREATED_BEFORE)
+ .setDescription("To retrieve issues created before the given date (exclusive). Format: date or datetime ISO formats")
+ .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
+ action.createParam(IssueFilterParameters.PAGE_SIZE)
+ .setDescription("Maximum number of results per page. " +
+ "Default value: 100 (except when the 'components' parameter is set, value is set to \"-1\" in this case). " +
+ "If set to \"-1\", the max possible value is used")
+ .setExampleValue("50");
+ action.createParam(IssueFilterParameters.PAGE_INDEX)
+ .setDescription("Index of the selected page")
+ .setDefaultValue("1")
+ .setExampleValue("2");
+ action.createParam(IssueFilterParameters.SORT)
+ .setDescription("Sort field")
+ .setPossibleValues(IssueQuery.SORTS);
+ action.createParam(IssueFilterParameters.ASC)
+ .setDescription("Ascending sort")
+ .setBooleanPossibleValues();
+ }
+
+ @Override
+ public void handle(Request request, Response response) {
+ IssueQueryResult queryResult = issueFinder.find(createQuery(request));
+
+ JsonWriter json = response.newJsonWriter();
+ json.beginObject();
+
+ writePaging(queryResult, json);
+ writeIssues(queryResult, request.paramAsStrings(EXTRA_FIELDS_PARAM), json);
+ writeComponents(queryResult, json);
+ writeProjects(queryResult, json);
+ writeRules(queryResult, json);
+ writeUsers(queryResult, json);
+
+ json.endObject().close();
+ }
+
+ private void writePaging(IssueQueryResult result, JsonWriter json) {
+ json.prop("maxResultsReached", result.maxResultsReached());
+ json.name("paging").beginObject()
+ .prop("pageIndex", result.paging().pageIndex())
+ .prop("pageSize", result.paging().pageSize())
+ .prop("total", result.paging().total())
+ .prop("fTotal", i18n.formatInteger(UserSession.get().locale(), result.paging().total()))
+ .prop("pages", result.paging().pages())
+ .endObject();
+ }
+
+ private void writeIssues(IssueQueryResult result, @Nullable List<String> extraFields, JsonWriter json) {
+ json.name("issues").beginArray();
+
+ for (Issue i : result.issues()) {
+ json.beginObject();
+
+ DefaultIssue issue = (DefaultIssue) i;
+ String actionPlanKey = issue.actionPlanKey();
+ Duration debt = issue.debt();
+ Date updateDate = issue.updateDate();
+ Date closeDate = issue.closeDate();
+
+ json
+ .prop("key", issue.key())
+ .prop("component", issue.componentKey())
+ .prop("project", issue.projectKey())
+ .prop("rule", issue.ruleKey().toString())
+ .prop("status", issue.status())
+ .prop("resolution", issue.resolution())
+ .prop("severity", issue.severity())
+ .prop("message", issue.message())
+ .prop("line", issue.line())
+ .prop("debt", debt != null ? durations.format(UserSession.get().locale(), debt, Durations.DurationFormat.SHORT) : null)
+ .prop("reporter", issue.reporter())
+ .prop("assignee", issue.assignee())
+ .prop("author", issue.authorLogin())
+ .prop("actionPlan", actionPlanKey)
+ .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
+ .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null)
+ .prop("fUpdateAge", formatAgeDate(updateDate))
+ .prop("closeDate", closeDate != null ? DateUtils.formatDateTime(closeDate) : null);
+
+ writeIssueAttributes(issue, json);
+ writeIssueExtraFields(result, issue, extraFields, json);
+ json.endObject();
+ }
+
+ json.endArray();
+ }
+
+ private void writeIssueAttributes(Issue issue, JsonWriter json) {
+ if (!issue.attributes().isEmpty()) {
+ json.name("attr").beginObject();
+ for (Map.Entry<String, String> entry : issue.attributes().entrySet()) {
+ json.prop(entry.getKey(), entry.getValue());
+ }
+ json.endObject();
+ }
+ }
+
+ private void writeIssueExtraFields(IssueQueryResult result, Issue issue, @Nullable List<String> extraFields, JsonWriter json) {
+ if (extraFields != null) {
+ if (extraFields.contains("actions")) {
+ actionsWriter.writeActions(issue, json);
+ }
+ if (extraFields.contains("transitions")) {
+ actionsWriter.writeTransitions(issue, json);
+ }
+ String assignee = issue.assignee();
+ if (extraFields.contains("assigneeName") && assignee != null) {
+ User user = result.user(assignee);
+ json.prop("assigneeName", user != null ? user.name() : null);
+ }
+ String actionPlanKey = issue.actionPlanKey();
+ if (extraFields.contains("actionPlanName") && actionPlanKey != null) {
+ ActionPlan actionPlan = result.actionPlan(issue);
+ json.prop("actionPlanName", actionPlan != null ? actionPlan.name() : null);
+ }
+ }
+ }
+
+ private void writeComponents(IssueQueryResult result, JsonWriter json) {
+ json.name("components").beginArray();
+ for (Component component : result.components()) {
+ ComponentDto componentDto = (ComponentDto) component;
+ json.beginObject()
+ .prop("key", component.key())
+ .prop("id", componentDto.getId())
+ .prop("qualifier", component.qualifier())
+ .prop("name", component.name())
+ .prop("longName", component.longName())
+ .prop("path", component.path())
+ // On a root project, subProjectId is null but projectId is equal to itself, which make no sense.
+ .prop("projectId", (componentDto.projectId() != null && componentDto.subProjectId() != null) ? componentDto.projectId() : null)
+ .prop("subProjectId", componentDto.subProjectId())
+ .endObject();
+ }
+ json.endArray();
+ }
+
+ private void writeProjects(IssueQueryResult result, JsonWriter json) {
+ json.name("projects").beginArray();
+ for (Component project : result.projects()) {
+ ComponentDto componentDto = (ComponentDto) project;
+ json.beginObject()
+ .prop("key", project.key())
+ .prop("id", componentDto.getId())
+ .prop("qualifier", project.qualifier())
+ .prop("name", project.name())
+ .prop("longName", project.longName())
+ .endObject();
+ }
+ json.endArray();
+ }
+
+ private void writeRules(IssueQueryResult result, JsonWriter json) {
+ json.name("rules").beginArray();
+ for (Rule rule : result.rules()) {
+ json.beginObject()
+ .prop("key", rule.ruleKey().toString())
+ .prop("name", rule.getName())
+ .prop("desc", rule.getDescription())
+ .prop("status", rule.getStatus())
+ .endObject();
+ }
+ json.endArray();
+ }
+
+ private void writeUsers(IssueQueryResult result, JsonWriter json) {
+ json.name("users").beginArray();
+ for (User user : result.users()) {
+ json.beginObject()
+ .prop("login", user.login())
+ .prop("name", user.name())
+ .prop("active", user.active())
+ .prop("email", user.email())
+ .endObject();
+ }
+ json.endArray();
+ }
+
+
+//
+// private void writeTransitions(Issue issue, JsonWriter json) {
+// json.name("transitions").beginArray();
+// if (UserSession.get().isLoggedIn()) {
+// List<Transition> transitions = issueService.listTransitions(issue, UserSession.get());
+// for (Transition transition : transitions) {
+// json.value(transition.key());
+// }
+// }
+// json.endArray();
+// }
+//
+// private void writeActions(DefaultIssue issue, JsonWriter json) {
+// json.name("actions").beginArray();
+// for (String action : actions(issue)) {
+// json.value(action);
+// }
+// json.endArray();
+// }
+//
+// private List<String> actions(DefaultIssue issue) {
+// List<String> actions = newArrayList();
+// String login = UserSession.get().login();
+// if (login != null) {
+// actions.add("comment");
+// if (issue.resolution() == null) {
+// actions.add("assign");
+// if (!login.equals(issue.assignee())) {
+// actions.add("assign_to_me");
+// }
+// actions.add("plan");
+// String projectKey = issue.projectKey();
+// if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) {
+// actions.add("set_severity");
+// }
+// for (Action action : actionService.listAvailableActions(issue)) {
+// actions.add(action.key());
+// }
+// }
+// }
+// return actions;
+// }
+//
+
+ @CheckForNull
+ private String formatAgeDate(@Nullable Date date) {
+ if (date != null) {
+ return i18n.ageFromNow(UserSession.get().locale(), date);
+ }
+ return null;
+ }
+
+ @CheckForNull
+ private static Collection<RuleKey> stringsToRules(@Nullable Collection<String> rules) {
+ if (rules != null) {
+ return newArrayList(Iterables.transform(rules, new Function<String, RuleKey>() {
+ @Override
+ public RuleKey apply(@Nullable String s) {
+ return s != null ? RuleKey.parse(s) : null;
+ }
+ }));
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ static IssueQuery createQuery(Request request) {
+ IssueQuery.Builder builder = IssueQuery.builder()
+ .requiredRole(UserRole.USER)
+ .issueKeys(request.paramAsStrings(IssueFilterParameters.ISSUES))
+ .severities(request.paramAsStrings(IssueFilterParameters.SEVERITIES))
+ .statuses(request.paramAsStrings(IssueFilterParameters.STATUSES))
+ .resolutions(request.paramAsStrings(IssueFilterParameters.RESOLUTIONS))
+ .resolved(request.paramAsBoolean(IssueFilterParameters.RESOLVED))
+ .components(request.paramAsStrings(IssueFilterParameters.COMPONENTS))
+ .componentRoots(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOTS))
+ .rules(stringsToRules(request.paramAsStrings(IssueFilterParameters.RULES)))
+ .actionPlans(request.paramAsStrings(IssueFilterParameters.ACTION_PLANS))
+ .reporters(request.paramAsStrings(IssueFilterParameters.REPORTERS))
+ .assignees(request.paramAsStrings(IssueFilterParameters.ASSIGNEES))
+ .languages(request.paramAsStrings(IssueFilterParameters.LANGUAGES))
+ .assigned(request.paramAsBoolean(IssueFilterParameters.ASSIGNED))
+ .planned(request.paramAsBoolean(IssueFilterParameters.PLANNED))
+ .hideRules(request.paramAsBoolean(IssueFilterParameters.HIDE_RULES))
+ .createdAt(request.paramAsDate(IssueFilterParameters.CREATED_AT))
+ .createdAfter(request.paramAsDate(IssueFilterParameters.CREATED_AFTER))
+ .createdBefore(request.paramAsDate(IssueFilterParameters.CREATED_BEFORE))
+ .pageSize(request.paramAsInt(IssueFilterParameters.PAGE_SIZE))
+ .pageIndex(request.paramAsInt(IssueFilterParameters.PAGE_INDEX));
+ String sort = request.param(IssueFilterParameters.SORT);
+ if (!Strings.isNullOrEmpty(sort)) {
+ builder.sort(sort);
+ builder.asc(request.paramAsBoolean(IssueFilterParameters.ASC));
+ }
+ return builder.build();
+ }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java
index ec121b7c206..2a94bd9b2ac 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java
@@ -25,7 +25,6 @@ import com.google.common.io.Resources;
import org.sonar.api.component.Component;
import org.sonar.api.i18n.I18n;
import org.sonar.api.issue.*;
-import org.sonar.api.issue.action.Action;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.FieldDiffs;
import org.sonar.api.server.debt.DebtCharacteristic;
@@ -41,14 +40,11 @@ import org.sonar.api.utils.Durations;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
-import org.sonar.core.issue.workflow.Transition;
import org.sonar.markdown.Markdown;
import org.sonar.server.debt.DebtModelService;
import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.issue.ActionService;
import org.sonar.server.issue.IssueChangelog;
import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueService;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
@@ -58,24 +54,20 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
-import static com.google.common.collect.Lists.newArrayList;
-
public class IssueShowAction implements RequestHandler {
private final IssueFinder issueFinder;
- private final IssueService issueService;
private final IssueChangelogService issueChangelogService;
- private final ActionService actionService;
+ private final IssueActionsWriter actionsWriter;
private final DebtModelService debtModel;
private final I18n i18n;
private final Durations durations;
- public IssueShowAction(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService,
+ public IssueShowAction(IssueFinder issueFinder, IssueChangelogService issueChangelogService, IssueActionsWriter actionsWriter,
DebtModelService debtModel, I18n i18n, Durations durations) {
this.issueFinder = issueFinder;
- this.issueService = issueService;
this.issueChangelogService = issueChangelogService;
- this.actionService = actionService;
+ this.actionsWriter = actionsWriter;
this.debtModel = debtModel;
this.i18n = i18n;
this.durations = durations;
@@ -96,7 +88,9 @@ public class IssueShowAction implements RequestHandler {
@Override
public void handle(Request request, Response response) {
String issueKey = request.mandatoryParam("key");
- IssueQueryResult queryResult = issueFinder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).build());
+ IssueQueryResult queryResult = issueFinder.find(IssueQuery.builder()
+ .requiredRole(UserRole.USER)
+ .issueKeys(Arrays.asList(issueKey)).build());
if (queryResult.issues().size() != 1) {
throw new NotFoundException("Issue not found: " + issueKey);
}
@@ -106,8 +100,8 @@ public class IssueShowAction implements RequestHandler {
json.beginObject().name("issue").beginObject();
writeIssue(queryResult, issue, json);
- writeTransitions(issue, json);
- writeActions(issue, json);
+ actionsWriter.writeActions(issue, json);
+ actionsWriter.writeTransitions(issue, json);
writeComments(queryResult, issue, json);
writeChangelog(issue, json);
@@ -220,48 +214,6 @@ public class IssueShowAction implements RequestHandler {
return null;
}
- private void writeTransitions(Issue issue, JsonWriter json) {
- json.name("transitions").beginArray();
- if (UserSession.get().isLoggedIn()) {
- List<Transition> transitions = issueService.listTransitions(issue, UserSession.get());
- for (Transition transition : transitions) {
- json.value(transition.key());
- }
- }
- json.endArray();
- }
-
- private void writeActions(DefaultIssue issue, JsonWriter json) {
- json.name("actions").beginArray();
- for (String action : actions(issue)) {
- json.value(action);
- }
- json.endArray();
- }
-
- private List<String> actions(DefaultIssue issue) {
- List<String> actions = newArrayList();
- String login = UserSession.get().login();
- if (login != null) {
- actions.add("comment");
- if (issue.resolution() == null) {
- actions.add("assign");
- if (!login.equals(issue.assignee())) {
- actions.add("assign_to_me");
- }
- actions.add("plan");
- String projectKey = issue.projectKey();
- if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) {
- actions.add("set_severity");
- }
- for (Action action : actionService.listAvailableActions(issue)) {
- actions.add(action.key());
- }
- }
- }
- return actions;
- }
-
private void writeComments(IssueQueryResult queryResult, Issue issue, JsonWriter json) {
json.name("comments").beginArray();
String login = UserSession.get().login();
@@ -311,7 +263,7 @@ public class IssueShowAction implements RequestHandler {
json.endArray();
}
- private void addUserWithLabel(IssueQueryResult result, @Nullable String value, String field, JsonWriter json) {
+ private static void addUserWithLabel(IssueQueryResult result, @Nullable String value, String field, JsonWriter json) {
if (value != null) {
User user = result.user(value);
json
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java
index 302d5a0c487..d7de0ebe9db 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java
@@ -21,17 +21,18 @@ package org.sonar.server.issue.ws;
import com.google.common.io.Resources;
import org.sonar.api.issue.DefaultTransitions;
-import org.sonar.api.issue.Issue;
import org.sonar.api.rule.Severity;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
public class IssuesWs implements WebService {
- private final IssueShowAction showHandler;
+ private final IssueShowAction showAction;
+ private final IssueSearchAction searchAction;
- public IssuesWs(IssueShowAction showHandler) {
- this.showHandler = showHandler;
+ public IssuesWs(IssueShowAction showAction, IssueSearchAction searchAction) {
+ this.showAction = showAction;
+ this.searchAction = searchAction;
}
@Override
@@ -40,8 +41,9 @@ public class IssuesWs implements WebService {
controller.setDescription("Coding rule issues");
controller.setSince("3.6");
- showHandler.define(controller);
- defineSearchAction(controller);
+ showAction.define(controller);
+ searchAction.define(controller);
+
defineChangelogAction(controller);
defineAssignAction(controller);
defineAddCommentAction(controller);
@@ -58,80 +60,6 @@ public class IssuesWs implements WebService {
controller.done();
}
- private void defineSearchAction(NewController controller) {
- WebService.NewAction action = controller.createAction("search")
- .setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. " +
- "Requires Browse permission on project(s)")
- .setSince("3.6")
- .setHandler(RailsHandler.INSTANCE)
- .setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
-
- action.createParam("issues")
- .setDescription("Comma-separated list of issue keys")
- .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
- action.createParam("severities")
- .setDescription("Comma-separated list of severities")
- .setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
- .setPossibleValues(Severity.ALL);
- action.createParam("statuses")
- .setDescription("Comma-separated list of statuses")
- .setExampleValue(Issue.STATUS_OPEN + "," + Issue.STATUS_REOPENED)
- .setPossibleValues(Issue.STATUSES);
- action.createParam("resolutions")
- .setDescription("Comma-separated list of resolutions")
- .setExampleValue(Issue.RESOLUTION_FIXED + "," + Issue.RESOLUTION_REMOVED)
- .setPossibleValues(Issue.RESOLUTIONS);
- action.createParam("resolved")
- .setDescription("To match resolved or unresolved issues")
- .setBooleanPossibleValues();
- action.createParam("components")
- .setDescription("To retrieve issues associated to a specific list of components (comma-separated list of component keys). " +
- "Note that if you set the value to a project key, only issues associated to this project are retrieved. " +
- "Issues associated to its sub-components (such as files, packages, etc.) are not retrieved. See also componentRoots")
- .setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
- action.createParam("componentRoots")
- .setDescription("To retrieve issues associated to a specific list of components and their sub-components (comma-separated list of component keys). " +
- "Views are not supported")
- .setExampleValue("org.apache.struts:struts");
- action.createParam("rules")
- .setDescription("Comma-separated list of coding rule keys. Format is <repository>:<rule>")
- .setExampleValue("squid:AvoidCycles");
- action.createParam("actionPlans")
- .setDescription("Comma-separated list of action plan keys (not names)")
- .setExampleValue("3f19de90-1521-4482-a737-a311758ff513");
- action.createParam("planned")
- .setDescription("To retrieve issues associated to an action plan or not")
- .setBooleanPossibleValues();
- action.createParam("reporters")
- .setDescription("Comma-separated list of reporter logins")
- .setExampleValue("admin");
- action.createParam("assignees")
- .setDescription("Comma-separated list of assignee logins")
- .setExampleValue("admin,usera");
- action.createParam("assigned")
- .setDescription("To retrieve assigned or unassigned issues")
- .setBooleanPossibleValues();
- action.createParam("extra_fields")
- .setDescription("Add some extra fields on each issue. Available since 4.4")
- .setPossibleValues("actions", "transitions");
- action.createParam("createdAfter")
- .setDescription("To retrieve issues created after the given date (inclusive). Format: date or datetime ISO formats")
- .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
- action.createParam("createdBefore")
- .setDescription("To retrieve issues created before the given date (exclusive). Format: date or datetime ISO formats")
- .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
- action.createParam("pageSize")
- .setDescription("Maximum number of results per page. " +
- "Default value: 100 (except when the 'components' parameter is set, value is set to \"-1\" in this case). " +
- "If set to \"-1\", the max possible value is used")
- .setExampleValue("50");
- action.createParam("pageIndex")
- .setDescription("Index of the selected page")
- .setDefaultValue("1")
- .setExampleValue("2");
- RailsHandler.addFormatParam(action);
- }
-
private void defineChangelogAction(NewController controller) {
WebService.NewAction action = controller.createAction("changelog")
.setDescription("Display changelog of an issue")
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
index 180cbcfdf31..99fed3068f4 100644
--- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
+++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
@@ -96,6 +96,8 @@ import org.sonar.server.issue.actionplan.ActionPlanWs;
import org.sonar.server.issue.filter.IssueFilterService;
import org.sonar.server.issue.filter.IssueFilterWriter;
import org.sonar.server.issue.filter.IssueFilterWs;
+import org.sonar.server.issue.ws.IssueActionsWriter;
+import org.sonar.server.issue.ws.IssueSearchAction;
import org.sonar.server.issue.ws.IssueShowAction;
import org.sonar.server.issue.ws.IssuesWs;
import org.sonar.server.measure.MeasureFilterEngine;
@@ -421,8 +423,10 @@ class ServerComponents {
pico.addSingleton(Actions.class);
pico.addSingleton(IssueBulkChangeService.class);
pico.addSingleton(IssueChangelogFormatter.class);
- pico.addSingleton(IssueShowAction.class);
pico.addSingleton(IssuesWs.class);
+ pico.addSingleton(IssueShowAction.class);
+ pico.addSingleton(IssueSearchAction.class);
+ pico.addSingleton(IssueActionsWriter.class);
// issue filters
pico.addSingleton(IssueFilterService.class);
diff --git a/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json b/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json
index d22e748ea58..8c248e73108 100644
--- a/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json
+++ b/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json
@@ -29,6 +29,9 @@
"createdAt": "2013-05-13T18:08:34+0200"
}
],
+ "attr": {
+ "jira-issue-key": "SONAR-1234"
+ },
"actions": ["link-to-jira"],
"transitions": [
"unconfirm",
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
new file mode 100644
index 00000000000..bb8d97e87b0
--- /dev/null
+++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
@@ -0,0 +1,209 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.ws;
+
+import org.json.JSONException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.action.Action;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.workflow.Transition;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.user.UserSession;
+
+import java.io.StringWriter;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class IssueActionsWriterTest {
+
+ @Mock
+ IssueService issueService;
+
+ @Mock
+ ActionService actionService;
+
+ IssueActionsWriter writer;
+
+ @Before
+ public void setUp() throws Exception {
+ writer = new IssueActionsWriter(issueService, actionService);
+ }
+
+ @Test
+ public void write_all_standard_actions() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+
+ MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, "sample");
+
+ testActions(issue,
+ "{\"actions\": " +
+ "[" +
+ "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"set_severity\"\n" +
+ "]}"
+ );
+ }
+
+ @Test
+ public void write_plugin_actions() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+
+ MockUserSession.set().setLogin("john");
+ Action action = mock(Action.class);
+ when(action.key()).thenReturn("link-to-jira");
+ when(actionService.listAvailableActions(eq(issue))).thenReturn(newArrayList(action));
+
+ testActions(issue,
+ "{\"actions\": " +
+ "[" +
+ "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"link-to-jira\"\n" +
+ "]}"
+ );
+ }
+
+ @Test
+ public void write_only_comment_action() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setResolution("CLOSED");
+
+ MockUserSession.set().setLogin("john");
+
+ testActions(issue,
+ "{\"actions\": " +
+ "[" +
+ "\"comment\"" +
+ "]}"
+ );
+ }
+
+ @Test
+ public void write_no_action_if_not_logged() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+
+ MockUserSession.set();
+
+ testActions(issue,
+ "{\"actions\": []}"
+ );
+ }
+
+ @Test
+ public void write_actions_without_assign_to_me() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setAssignee("john");
+
+ MockUserSession.set().setLogin("john");
+
+ testActions(issue,
+ "{\"actions\": " +
+ "[" +
+ "\"comment\", \"assign\", \"plan\"\n" +
+ "]}"
+ );
+ }
+
+ @Test
+ public void write_transitions() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+
+ when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN")));
+ MockUserSession.set().setLogin("john");
+
+ testTransitions(issue,
+ "{\"transitions\": [\n" +
+ " \"reopen\"\n" +
+ " ]}"
+ );
+ }
+
+ @Test
+ public void write_no_transitions() throws Exception {
+ Issue issue = new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+
+ MockUserSession.set().setLogin("john");
+
+ testTransitions(issue,
+ "{\"transitions\": []}"
+ );
+ }
+
+ private void testActions(Issue issue, String expected) throws JSONException {
+ StringWriter output = new StringWriter();
+ JsonWriter jsonWriter = JsonWriter.of(output);
+ jsonWriter.beginObject();
+ writer.writeActions(issue, jsonWriter);
+ jsonWriter.endObject();
+ JSONAssert.assertEquals(output.toString(), expected, true);
+ }
+
+ private void testTransitions(Issue issue, String expected) throws JSONException {
+ StringWriter output = new StringWriter();
+ JsonWriter jsonWriter = JsonWriter.of(output);
+ jsonWriter.beginObject();
+ writer.writeTransitions(issue, jsonWriter);
+ jsonWriter.endObject();
+ JSONAssert.assertEquals(output.toString(), expected, true);
+ }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java
new file mode 100644
index 00000000000..5eecc61fbef
--- /dev/null
+++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java
@@ -0,0 +1,377 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.ws;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.component.Component;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueFinder;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.user.User;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
+import org.sonar.api.utils.Paging;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.DefaultActionPlan;
+import org.sonar.core.issue.DefaultIssueQueryResult;
+import org.sonar.core.issue.workflow.Transition;
+import org.sonar.core.user.DefaultUser;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.*;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class IssueSearchActionTest {
+
+ @Mock
+ IssueFinder issueFinder;
+
+ @Mock
+ IssueService issueService;
+
+ @Mock
+ ActionService actionService;
+
+ @Mock
+ I18n i18n;
+
+ @Mock
+ Durations durations;
+
+ List<Issue> issues;
+ DefaultIssueQueryResult result;
+
+ Date issueCreationDate;
+
+ WsTester tester;
+
+ @Before
+ public void setUp() throws Exception {
+ issues = new ArrayList<Issue>();
+ result = new DefaultIssueQueryResult(issues);
+ when(issueFinder.find(any(IssueQuery.class))).thenReturn(result);
+
+ issueCreationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
+ when(i18n.formatDateTime(any(Locale.class), eq(issueCreationDate))).thenReturn("Jan 22, 2014 10:03 AM");
+
+ result.setMaxResultsReached(true);
+ result.setPaging(Paging.create(100, 1, 2));
+ when(i18n.formatInteger(any(Locale.class), eq(2))).thenReturn("2");
+
+ tester = new WsTester(new IssuesWs(mock(IssueShowAction.class), new IssueSearchAction(issueFinder, new IssueActionsWriter(issueService, actionService), i18n, durations)));
+ }
+
+ @Test
+ public void issues() throws Exception {
+ String issueKey = "ABCD";
+ Issue issue = new DefaultIssue()
+ .setKey(issueKey)
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setActionPlanKey("AP-ABCD")
+ .setLine(12)
+ .setEffortToFix(2.0)
+ .setMessage("Fix it")
+ .setResolution("FIXED")
+ .setStatus("CLOSED")
+ .setSeverity("MAJOR")
+ .setAssignee("john")
+ .setReporter("steven")
+ .setAuthorLogin("Henry")
+ .setCreationDate(issueCreationDate);
+ issues.add(issue);
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues.json");
+ }
+
+ @Test
+ public void issues_with_components() throws Exception {
+ String issueKey = "ABCD";
+ Issue issue = new DefaultIssue()
+ .setKey(issueKey)
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setActionPlanKey("AP-ABCD")
+ .setLine(12)
+ .setEffortToFix(2.0)
+ .setMessage("Fix it")
+ .setResolution("FIXED")
+ .setStatus("CLOSED")
+ .setSeverity("MAJOR")
+ .setAssignee("john")
+ .setReporter("steven")
+ .setAuthorLogin("Henry")
+ .setCreationDate(issueCreationDate);
+ issues.add(issue);
+
+ ComponentDto component = new ComponentDto()
+ .setId(10L)
+ .setKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setLongName("src/main/xoo/sample/Sample.xoo")
+ .setName("Sample.xoo")
+ .setQualifier("FIL")
+ .setPath("src/main/xoo/sample/Sample.xoo")
+ .setSubProjectId(7L)
+ .setProjectId(7L);
+
+ ComponentDto project = new ComponentDto()
+ .setId(7L)
+ .setKey("sample")
+ .setLongName("Sample")
+ .setName("Sample")
+ .setQualifier("TRK")
+ .setProjectId(7L);
+
+ result.addComponents(Lists.<Component>newArrayList(component, project));
+ result.addProjects(Lists.<Component>newArrayList(project));
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_components.json");
+ }
+
+ @Test
+ public void issues_with_rules() throws Exception {
+ String issueKey = "ABCD";
+ Issue issue = new DefaultIssue()
+ .setKey(issueKey)
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setActionPlanKey("AP-ABCD")
+ .setLine(12)
+ .setEffortToFix(2.0)
+ .setMessage("Fix it")
+ .setResolution("FIXED")
+ .setStatus("CLOSED")
+ .setSeverity("MAJOR")
+ .setAssignee("john")
+ .setReporter("steven")
+ .setAuthorLogin("Henry")
+ .setCreationDate(issueCreationDate);
+ issues.add(issue);
+
+ result.addRules(newArrayList(
+ Rule.create("squid", "AvoidCycle").setName("Avoid cycle").setDescription("Avoid cycle description")
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_rules.json");
+ }
+
+ @Test
+ public void issues_with_users() throws Exception {
+ String issueKey = "ABCD";
+ Issue issue = new DefaultIssue()
+ .setKey(issueKey)
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setActionPlanKey("AP-ABCD")
+ .setLine(12)
+ .setEffortToFix(2.0)
+ .setMessage("Fix it")
+ .setResolution("FIXED")
+ .setStatus("CLOSED")
+ .setSeverity("MAJOR")
+ .setAssignee("john")
+ .setReporter("steven")
+ .setAuthorLogin("Henry")
+ .setCreationDate(issueCreationDate);
+ issues.add(issue);
+
+ result.addUsers(Lists.<User>newArrayList(
+ new DefaultUser().setName("John").setLogin("john").setActive(true).setEmail("john@email.com")
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_users.json");
+ }
+
+ @Test
+ public void issues_with_dates() throws Exception {
+ Date creationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
+ Date updateDate = DateUtils.parseDateTime("2014-01-23T19:10:03+0100");
+ Date closedDate = DateUtils.parseDateTime("2014-01-24T19:10:03+0100");
+
+ Issue issue = createStandardIssue()
+ .setCreationDate(creationDate)
+ .setUpdateDate(updateDate)
+ .setCloseDate(closedDate);
+ issues.add(issue);
+
+ when(i18n.formatDateTime(any(Locale.class), eq(creationDate))).thenReturn("Jan 22, 2014 10:03 AM");
+ when(i18n.formatDateTime(any(Locale.class), eq(updateDate))).thenReturn("Jan 23, 2014 10:03 AM");
+ when(i18n.ageFromNow(any(Locale.class), eq(updateDate))).thenReturn("9 days");
+ when(i18n.formatDateTime(any(Locale.class), eq(closedDate))).thenReturn("Jan 24, 2014 10:03 AM");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_dates.json");
+ }
+
+ @Test
+ public void issues_with_debt() throws Exception {
+ Duration debt = (Duration.create(7260L));
+ Issue issue = createStandardIssue().setDebt(debt);
+ issues.add(issue);
+
+ when(durations.format(any(Locale.class), eq(debt), eq(Durations.DurationFormat.SHORT))).thenReturn("2 hours 1 minutes");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_debt.json");
+ }
+
+ @Test
+ public void issues_with_action_plan() throws Exception {
+ Issue issue = createStandardIssue()
+ .setActionPlanKey("AP-ABCD");
+ issues.add(issue);
+
+ MockUserSession.set();
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_action_plan.json");
+ }
+
+ @Test
+ public void issues_with_attributes() throws Exception {
+ Issue issue = createStandardIssue()
+ .setAttribute("jira-issue-key", "SONAR-1234");
+ issues.add(issue);
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search");
+ request.execute().assertJson(getClass(), "issues_with_attributes.json");
+ }
+
+ @Test
+ public void issues_with_extra_fields() throws Exception {
+ Issue issue = createStandardIssue()
+ .setActionPlanKey("AP-ABCD")
+ .setAssignee("john");
+ issues.add(issue);
+
+ MockUserSession.set().setLogin("john");
+ when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN")));
+
+ result.addActionPlans(newArrayList((ActionPlan) new DefaultActionPlan().setKey("AP-ABCD").setName("1.0")));
+
+ result.addUsers(Lists.<User>newArrayList(
+ new DefaultUser().setName("John").setLogin("john")
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search").setParam("extra_fields", "actions,transitions,assigneeName,actionPlanName");
+ request.execute().assertJson(getClass(), "issues_with_extra_fields.json");
+ }
+
+ @Test
+ public void verify_issue_query_parameters() throws Exception {
+ Map<String, String> map = newHashMap();
+ map.put("issues", "ABCDE1234");
+ map.put("severities", "MAJOR,MINOR");
+ map.put("statuses", "CLOSED");
+ map.put("resolutions", "FALSE-POSITIVE");
+ map.put("resolved", "true");
+ map.put("components", "org.apache");
+ map.put("componentRoots", "org.sonar");
+ map.put("reporters", "marilyn");
+ map.put("assignees", "joanna");
+ map.put("languages", "xoo");
+ map.put("assigned", "true");
+ map.put("planned", "true");
+ map.put("hideRules", "true");
+ map.put("createdAt", "2013-04-15T09:08:24+0200");
+ map.put("createdAfter", "2013-04-16T09:08:24+0200");
+ map.put("createdBefore", "2013-04-17T09:08:24+0200");
+ map.put("rules", "squid:AvoidCycle,findbugs:NullReference");
+ map.put("pageSize", "10");
+ map.put("pageIndex", "50");
+ map.put("sort", "CREATION_DATE");
+ map.put("asc", "true");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/issues", "search").setParams(map);
+ request.execute();
+
+ ArgumentCaptor<IssueQuery> captor = ArgumentCaptor.forClass(IssueQuery.class);
+ verify(issueFinder).find(captor.capture());
+
+ IssueQuery query = captor.getValue();
+ assertThat(query.requiredRole()).isEqualTo("user");
+ assertThat(query.issueKeys()).containsOnly("ABCDE1234");
+ assertThat(query.severities()).containsOnly("MAJOR", "MINOR");
+ assertThat(query.statuses()).containsOnly("CLOSED");
+ assertThat(query.resolutions()).containsOnly("FALSE-POSITIVE");
+ assertThat(query.resolved()).isTrue();
+ assertThat(query.components()).containsOnly("org.apache");
+ assertThat(query.componentRoots()).containsOnly("org.sonar");
+ assertThat(query.reporters()).containsOnly("marilyn");
+ assertThat(query.assignees()).containsOnly("joanna");
+ assertThat(query.languages()).containsOnly("xoo");
+ assertThat(query.assigned()).isTrue();
+ assertThat(query.planned()).isTrue();
+ assertThat(query.hideRules()).isTrue();
+ assertThat(query.createdAt()).isEqualTo(DateUtils.parseDateTime("2013-04-15T09:08:24+0200"));
+ assertThat(query.createdAfter()).isEqualTo(DateUtils.parseDateTime("2013-04-16T09:08:24+0200"));
+ assertThat(query.createdBefore()).isEqualTo(DateUtils.parseDateTime("2013-04-17T09:08:24+0200"));
+ assertThat(query.rules()).containsOnly(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("findbugs", "NullReference"));
+ assertThat(query.pageSize()).isEqualTo(10);
+ assertThat(query.pageIndex()).isEqualTo(50);
+ assertThat(query.sort()).isEqualTo("CREATION_DATE");
+ assertThat(query.asc()).isTrue();
+ }
+
+ private DefaultIssue createStandardIssue() {
+ return createIssue();
+ }
+
+ private DefaultIssue createIssue() {
+ return new DefaultIssue()
+ .setKey("ABCD")
+ .setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
+ .setProjectKey("sample")
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setCreationDate(issueCreationDate);
+ }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java
index fde5db793d7..35a83cb0ce7 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java
@@ -17,6 +17,7 @@
* 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.ws;
import com.google.common.collect.Lists;
@@ -31,7 +32,6 @@ import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueFinder;
import org.sonar.api.issue.IssueQuery;
-import org.sonar.api.issue.action.Action;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.DefaultIssueComment;
import org.sonar.api.issue.internal.FieldDiffs;
@@ -42,7 +42,6 @@ import org.sonar.api.user.User;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
-import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.issue.DefaultActionPlan;
import org.sonar.core.issue.DefaultIssueQueryResult;
@@ -95,7 +94,7 @@ public class IssueShowActionTest {
List<Issue> issues;
DefaultIssueQueryResult result;
- private Date issue_creation_date;
+ Date issueCreationDate;
WsTester tester;
@@ -108,12 +107,13 @@ public class IssueShowActionTest {
when(issueChangelogService.changelog(any(Issue.class))).thenReturn(mock(IssueChangelog.class));
- issue_creation_date = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
- when(i18n.formatDateTime(any(Locale.class), eq(issue_creation_date))).thenReturn("Jan 22, 2014 10:03 AM");
+ issueCreationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
+ when(i18n.formatDateTime(any(Locale.class), eq(issueCreationDate))).thenReturn("Jan 22, 2014 10:03 AM");
when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created");
- tester = new WsTester(new IssuesWs(new IssueShowAction(issueFinder, issueService, issueChangelogService, actionService, debtModel, i18n, durations)));
+ tester = new WsTester(new IssuesWs(new IssueShowAction(issueFinder, issueChangelogService, new IssueActionsWriter(issueService, actionService), debtModel, i18n, durations),
+ mock(IssueSearchAction.class)));
}
@Test
@@ -125,18 +125,18 @@ public class IssueShowActionTest {
.setProjectKey("org.sonar.Sonar")
.setRuleKey(RuleKey.of("squid", "AvoidCycle"))
.setLine(12)
- .setEffortToFix(2.0)
.setMessage("Fix it")
.setResolution("FIXED")
.setStatus("CLOSED")
.setSeverity("MAJOR")
- .setCreationDate(issue_creation_date);
+ .setCreationDate(issueCreationDate);
issues.add(issue);
result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
.setId(10L)
.setKey("org.sonar.server.issue.IssueClient")
.setLongName("SonarQube :: Issue Client")
+ .setName("SonarQube :: Issue Client")
.setQualifier("FIL")
.setSubProjectId(1L)
.setProjectId(1L)
@@ -146,6 +146,7 @@ public class IssueShowActionTest {
.setId(1L)
.setKey("org.sonar.Sonar")
.setLongName("SonarQube")
+ .setName("SonarQube")
.setProjectId(1L)
));
@@ -163,12 +164,11 @@ public class IssueShowActionTest {
.setProjectKey("org.sonar.Sonar")
.setRuleKey(RuleKey.of("squid", "AvoidCycle"))
.setLine(12)
- .setEffortToFix(2.0)
.setMessage("Fix it")
.setResolution("FIXED")
.setStatus("CLOSED")
.setSeverity("MAJOR")
- .setCreationDate(issue_creation_date);
+ .setCreationDate(issueCreationDate);
issues.add(issue);
// File
@@ -214,7 +214,7 @@ public class IssueShowActionTest {
.setResolution("FIXED")
.setStatus("CLOSED")
.setSeverity("MAJOR")
- .setCreationDate(issue_creation_date);
+ .setCreationDate(issueCreationDate);
issues.add(issue);
// File
@@ -445,59 +445,6 @@ public class IssueShowActionTest {
}
@Test
- public void show_issue_with_set_severity_action() throws Exception {
- DefaultIssue issue = createStandardIssue()
- .setStatus("OPEN");
- issues.add(issue);
-
- MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, issue.projectKey());
- WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key());
- request.execute().assertJson(getClass(), "show_issue_with_set_severity_action.json");
- }
-
- @Test
- public void show_issue_with_assign_to_me_action() throws Exception {
- DefaultIssue issue = createStandardIssue()
- .setStatus("OPEN");
- issues.add(issue);
-
- MockUserSession.set().setLogin("john");
- WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key());
- request.execute().assertJson(getClass(), "show_issue_with_assign_to_me_action.json");
- }
-
- @Test
- public void show_issue_without_assign_to_me_action() throws Exception {
- DefaultIssue issue = createStandardIssue()
- .setStatus("OPEN")
- .setAssignee("john");
- issues.add(issue);
-
- result.addUsers(Lists.<User>newArrayList(
- new DefaultUser().setLogin("john").setName("John")
- ));
-
- MockUserSession.set().setLogin("john");
- WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key());
- request.execute().assertJson(getClass(), "show_issue_without_assign_to_me_action.json");
- }
-
- @Test
- public void show_issue_with_actions_defined_by_plugins() throws Exception {
- Issue issue = createStandardIssue()
- .setStatus("OPEN");
- issues.add(issue);
-
- Action action = mock(Action.class);
- when(action.key()).thenReturn("link-to-jira");
- when(actionService.listAvailableActions(issue)).thenReturn(newArrayList(action));
-
- MockUserSession.set().setLogin("john");
- WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key());
- request.execute().assertJson(getClass(), "show_issue_with_actions_defined_by_plugins.json");
- }
-
- @Test
public void show_issue_with_changelog() throws Exception {
Issue issue = createStandardIssue();
issues.add(issue);
@@ -538,7 +485,7 @@ public class IssueShowActionTest {
.setComponentKey("org.sonar.server.issue.IssueClient")
.setProjectKey("org.sonar.Sonar")
.setRuleKey(RuleKey.of("squid", "AvoidCycle"))
- .setCreationDate(issue_creation_date);
+ .setCreationDate(issueCreationDate);
}
private void addComponentAndProject() {
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java
index 30d6babddc3..c425e7328eb 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java
@@ -27,9 +27,7 @@ import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.Durations;
import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.issue.ActionService;
import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueService;
import org.sonar.server.ws.WsTester;
import static org.fest.assertions.Assertions.assertThat;
@@ -39,13 +37,22 @@ public class IssuesWsTest {
IssueShowAction showAction;
+ IssueSearchAction searchAction;
+
WsTester tester;
@Before
public void setUp() throws Exception {
- showAction = new IssueShowAction(mock(IssueFinder.class), mock(IssueService.class), mock(IssueChangelogService.class), mock(ActionService.class),
- mock(DebtModelService.class), mock(I18n.class), mock(Durations.class));
- tester = new WsTester(new IssuesWs(showAction));
+ IssueFinder issueFinder = mock(IssueFinder.class);
+ IssueChangelogService issueChangelogService = mock(IssueChangelogService.class);
+ IssueActionsWriter actionsWriter = mock(IssueActionsWriter.class);
+ DebtModelService debtModelService = mock(DebtModelService.class);
+ I18n i18n = mock(I18n.class);
+ Durations durations = mock(Durations.class);
+
+ showAction = new IssueShowAction(issueFinder, issueChangelogService, actionsWriter, debtModelService, i18n, durations);
+ searchAction = new IssueSearchAction(issueFinder, actionsWriter, i18n, durations);
+ tester = new WsTester(new IssuesWs(showAction, searchAction));
}
@Test
@@ -87,9 +94,9 @@ public class IssuesWsTest {
assertThat(show.since()).isEqualTo("3.6");
assertThat(show.isPost()).isFalse();
assertThat(show.isInternal()).isFalse();
- assertThat(show.handler()).isInstanceOf(RailsHandler.class);
+ assertThat(show.handler()).isSameAs(searchAction);
assertThat(show.responseExampleAsString()).isNotEmpty();
- assertThat(show.params()).hasSize(19);
+ assertThat(show.params()).hasSize(23);
}
@Test
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json
new file mode 100644
index 00000000000..8d14e1cd9b4
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json
@@ -0,0 +1,32 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "resolution": "FIXED",
+ "status": "CLOSED",
+ "severity": "MAJOR",
+ "message": "Fix it",
+ "line": 12,
+ "reporter": "steven",
+ "assignee": "john",
+ "author": "Henry",
+ "actionPlan": "AP-ABCD",
+ "creationDate": "2014-01-22T19:10:03+0100"
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json
new file mode 100644
index 00000000000..6804fb0dcab
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json
@@ -0,0 +1,24 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "actionPlan" : "AP-ABCD",
+ "creationDate": "2014-01-22T19:10:03+0100"
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json
new file mode 100644
index 00000000000..9d03dcd4389
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json
@@ -0,0 +1,26 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "creationDate": "2014-01-22T19:10:03+0100",
+ "attr": {
+ "jira-issue-key": "SONAR-1234"
+ }
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json
new file mode 100644
index 00000000000..77991667809
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json
@@ -0,0 +1,58 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "resolution": "FIXED",
+ "status": "CLOSED",
+ "severity": "MAJOR",
+ "message": "Fix it",
+ "line": 12,
+ "reporter": "steven",
+ "assignee": "john",
+ "author": "Henry",
+ "actionPlan": "AP-ABCD",
+ "creationDate": "2014-01-22T19:10:03+0100"
+ }
+ ],
+ "components": [
+ {
+ "key": "sample:src/main/xoo/sample/Sample.xoo",
+ "id": 10,
+ "qualifier": "FIL",
+ "name": "Sample.xoo",
+ "longName": "src/main/xoo/sample/Sample.xoo",
+ "path": "src/main/xoo/sample/Sample.xoo",
+ "projectId": 7,
+ "subProjectId": 7
+ },
+ {
+ "key": "sample",
+ "id": 7,
+ "qualifier": "TRK",
+ "name": "Sample",
+ "longName": "Sample"
+ }
+ ],
+ "projects": [
+ {
+ "key": "sample",
+ "id": 7,
+ "qualifier": "TRK",
+ "name": "Sample",
+ "longName": "Sample"
+ }
+ ],
+ "rules": [],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json
new file mode 100644
index 00000000000..814789bc44c
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json
@@ -0,0 +1,26 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "creationDate": "2014-01-22T19:10:03+0100",
+ "updateDate": "2014-01-23T19:10:03+0100",
+ "fUpdateAge": "9 days",
+ "closeDate": "2014-01-24T19:10:03+0100"
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json
new file mode 100644
index 00000000000..e5054e61d64
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json
@@ -0,0 +1,24 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "debt": "2 hours 1 minutes",
+ "creationDate": "2014-01-22T19:10:03+0100"
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json
new file mode 100644
index 00000000000..399a702bb2e
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json
@@ -0,0 +1,39 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "creationDate": "2014-01-22T19:10:03+0100",
+ "assignee": "john",
+ "assigneeName" : "John",
+ "actionPlan" : "AP-ABCD",
+ "actionPlanName" : "1.0",
+ "actions": [
+ "comment", "assign", "plan"
+ ],
+ "transitions": [
+ "reopen"
+ ]
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": [
+ {
+ "login": "john",
+ "name": "John",
+ "active": false
+ }
+ ]
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json
new file mode 100644
index 00000000000..58568a53b53
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json
@@ -0,0 +1,39 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "resolution": "FIXED",
+ "status": "CLOSED",
+ "severity": "MAJOR",
+ "message": "Fix it",
+ "line": 12,
+ "reporter": "steven",
+ "assignee": "john",
+ "author": "Henry",
+ "actionPlan": "AP-ABCD",
+ "creationDate": "2014-01-22T19:10:03+0100"
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [
+ {
+ "key": "squid:AvoidCycle",
+ "name": "Avoid cycle",
+ "desc": "Avoid cycle description",
+ "status": "READY"
+ }
+ ],
+ "users": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json
new file mode 100644
index 00000000000..2d8c3ac6260
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json
@@ -0,0 +1,39 @@
+{
+ "maxResultsReached": true,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "fTotal": "2",
+ "pages": 1
+ },
+ "issues": [
+ {
+ "key": "ABCD",
+ "component": "sample:src/main/xoo/sample/Sample.xoo",
+ "project": "sample",
+ "rule": "squid:AvoidCycle",
+ "resolution": "FIXED",
+ "status": "CLOSED",
+ "severity": "MAJOR",
+ "message": "Fix it",
+ "line": 12,
+ "reporter": "steven",
+ "assignee": "john",
+ "author": "Henry",
+ "actionPlan": "AP-ABCD",
+ "creationDate": "2014-01-22T19:10:03+0100"
+ }
+ ],
+ "components": [],
+ "projects": [],
+ "rules": [],
+ "users": [
+ {
+ "login": "john",
+ "name": "John",
+ "active": true,
+ "email": "john@email.com"
+ }
+ ]
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json
deleted file mode 100644
index 9f83417b32a..00000000000
--- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "issue": {
- "key": "ABCD",
- "component": "org.sonar.server.issue.IssueClient",
- "componentLongName": "SonarQube :: Issue Client",
- "componentQualifier": "FIL",
- "project": "org.sonar.Sonar",
- "projectName": "SonarQube",
- "rule": "squid:AvoidCycle",
- "ruleName": "Avoid cycle",
- "status": "OPEN",
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "transitions": [],
- "actions": [
- "comment", "assign", "assign_to_me", "plan", "link-to-jira"
- ],
- "comments": [],
- "changelog": [
- {
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "diffs": ["Created"]
- }
- ]
- }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json
deleted file mode 100644
index eb7f97febca..00000000000
--- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "issue": {
- "key": "ABCD",
- "component": "org.sonar.server.issue.IssueClient",
- "componentLongName": "SonarQube :: Issue Client",
- "componentQualifier": "FIL",
- "project": "org.sonar.Sonar",
- "projectName": "SonarQube",
- "rule": "squid:AvoidCycle",
- "ruleName": "Avoid cycle",
- "status": "OPEN",
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "transitions": [],
- "actions": [
- "comment", "assign", "assign_to_me", "plan"
- ],
- "comments": [],
- "changelog": [
- {
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "diffs": ["Created"]
- }
- ]
- }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json
deleted file mode 100644
index 242d99c6a3e..00000000000
--- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "issue": {
- "key": "ABCD",
- "component": "org.sonar.server.issue.IssueClient",
- "componentLongName": "SonarQube :: Issue Client",
- "componentQualifier": "FIL",
- "project": "org.sonar.Sonar",
- "projectName": "SonarQube",
- "rule": "squid:AvoidCycle",
- "ruleName": "Avoid cycle",
- "status": "OPEN",
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "transitions": [],
- "actions": [
- "comment", "assign", "assign_to_me", "plan", "set_severity"
- ],
- "comments": [],
- "changelog": [
- {
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "diffs": ["Created"]
- }
- ]
- }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json
deleted file mode 100644
index ffde3996c13..00000000000
--- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "issue": {
- "key": "ABCD",
- "component": "org.sonar.server.issue.IssueClient",
- "componentLongName": "SonarQube :: Issue Client",
- "componentQualifier": "FIL",
- "project": "org.sonar.Sonar",
- "projectName": "SonarQube",
- "rule": "squid:AvoidCycle",
- "ruleName": "Avoid cycle",
- "assignee": "john",
- "assigneeName": "John",
- "status": "OPEN",
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "transitions": [],
- "actions": [
- "comment", "assign", "plan"
- ],
- "comments": [],
- "changelog": [
- {
- "creationDate": "2014-01-22T19:10:03+0100",
- "fCreationDate": "Jan 22, 2014 10:03 AM",
- "diffs": ["Created"]
- }
- ]
- }
-}