]> source.dussan.org Git - sonarqube.git/commitdiff
Rewrite some Ruby WS api/issues/{operation} in Java
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 4 Aug 2015 16:44:08 +0000 (18:44 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 5 Aug 2015 17:32:13 +0000 (19:32 +0200)
- api/issues/create
- api/issues/set_severity
- api/issues/plan
- api/issues/do_transition
- api/issues/plan

27 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentJsonWriter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/AssignAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/CreateAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueComponentHelper.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueWsModule.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/PlanAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/Search2Action.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAdditionalField.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/CreateActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/ws/DoTransitionActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueWsModuleTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/ws/PlanActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetSeverityActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/ws/ShowActionTest.java
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb

diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentJsonWriter.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentJsonWriter.java
deleted file mode 100644 (file)
index 01bf01a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.component.ws;
-
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.db.component.ComponentDto;
-
-public class ComponentJsonWriter {
-
-  public void write(JsonWriter json, ComponentDto component, ComponentDto project) {
-    json.beginObject()
-      .prop("uuid", component.uuid())
-      .prop("key", component.key())
-      .prop("id", component.getId())
-      .prop("qualifier", component.qualifier())
-      .prop("name", component.name())
-      .prop("longName", component.longName())
-      .prop("enabled", component.isEnabled())
-      .prop("path", component.path())
-      // On a root project, parentProjectId is null but projectId is equal to itself, which make no sense.
-      .prop("projectId", (component.projectUuid() != null && component.parentProjectId() != null) ? project.getId() : null)
-      // TODO replace with parentProjectId when sonar-ws-client is not used anymore by tests...
-      .prop("subProjectId", component.parentProjectId())
-      .endObject();
-  }
-}
index ecb4e0a02a3fcd97d96353f3145edd8a54c33166..67c580aa7e01f3c8fbab0d7f59b754c98165b087 100644 (file)
 package org.sonar.server.issue;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import java.io.StringWriter;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
 import org.sonar.api.issue.ActionPlan;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.IssueComment;
-import org.sonar.api.issue.action.Action;
-import org.sonar.api.rule.RuleKey;
 import org.sonar.api.server.ServerSide;
-import org.sonar.api.user.User;
 import org.sonar.api.utils.SonarException;
-import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.ActionPlanStats;
 import org.sonar.core.issue.DefaultActionPlan;
-import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.DefaultIssueComment;
 import org.sonar.core.issue.FieldDiffs;
 import org.sonar.core.issue.workflow.Transition;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.MyBatis;
-import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ResourceDao;
 import org.sonar.db.component.ResourceDto;
 import org.sonar.db.component.ResourceQuery;
 import org.sonar.db.issue.IssueFilterDto;
-import org.sonar.server.component.ws.ComponentJsonWriter;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.filter.IssueFilterParameters;
 import org.sonar.server.issue.filter.IssueFilterService;
-import org.sonar.server.issue.ws.IssueComponentHelper;
-import org.sonar.server.issue.ws.IssueJsonWriter;
 import org.sonar.server.search.QueryContext;
 import org.sonar.server.user.UserSession;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.user.ws.UserJsonWriter;
 import org.sonar.server.util.RubyUtils;
 import org.sonar.server.util.Validation;
 
-import static com.google.common.collect.Maps.newHashMap;
-
 /**
  * Used through ruby code <pre>Internal.issues</pre>
  * <p/>
@@ -100,50 +74,33 @@ public class InternalRubyIssueService {
 
   private static final String ACTION_PLANS_ERRORS_ACTION_PLAN_DOES_NOT_EXIST_MESSAGE = "action_plans.errors.action_plan_does_not_exist";
 
-  private static final List<String> ISSUE_FIELDS = ImmutableList.copyOf(IssueJsonWriter.SELECTABLE_FIELDS);
-
   private final IssueService issueService;
   private final IssueQueryService issueQueryService;
   private final IssueCommentService commentService;
   private final IssueChangelogService changelogService;
   private final ActionPlanService actionPlanService;
   private final ResourceDao resourceDao;
-  private final ActionService actionService;
   private final IssueFilterService issueFilterService;
   private final IssueBulkChangeService issueBulkChangeService;
-  private final IssueJsonWriter issueWriter;
-  private final IssueComponentHelper issueComponentHelper;
-  private final ComponentJsonWriter componentWriter;
-  private final UserIndex userIndex;
-  private final DbClient dbClient;
   private final UserSession userSession;
-  private final UserJsonWriter userWriter;
 
   public InternalRubyIssueService(
     IssueService issueService,
     IssueQueryService issueQueryService,
     IssueCommentService commentService,
     IssueChangelogService changelogService, ActionPlanService actionPlanService,
-    ResourceDao resourceDao, ActionService actionService,
+    ResourceDao resourceDao,
     IssueFilterService issueFilterService, IssueBulkChangeService issueBulkChangeService,
-    IssueJsonWriter issueWriter, IssueComponentHelper issueComponentHelper, ComponentJsonWriter componentWriter, UserIndex userIndex, DbClient dbClient,
-    UserSession userSession, UserJsonWriter userWriter) {
+    UserSession userSession) {
     this.issueService = issueService;
     this.issueQueryService = issueQueryService;
     this.commentService = commentService;
     this.changelogService = changelogService;
     this.actionPlanService = actionPlanService;
     this.resourceDao = resourceDao;
-    this.actionService = actionService;
     this.issueFilterService = issueFilterService;
     this.issueBulkChangeService = issueBulkChangeService;
-    this.issueWriter = issueWriter;
-    this.issueComponentHelper = issueComponentHelper;
-    this.componentWriter = componentWriter;
-    this.userIndex = userIndex;
-    this.dbClient = dbClient;
     this.userSession = userSession;
-    this.userWriter = userWriter;
   }
 
   public List<Transition> listTransitions(String issueKey) {
@@ -182,36 +139,6 @@ public class InternalRubyIssueService {
     return commentService.findComments(issueKeys);
   }
 
-  public Result<Issue> doTransition(String issueKey, String transitionKey) {
-    Result<Issue> result = Result.of();
-    try {
-      result.set(issueService.doTransition(issueKey, transitionKey));
-    } catch (Exception e) {
-      result.addError(e.getMessage());
-    }
-    return result;
-  }
-
-  public Result<Issue> setSeverity(String issueKey, String severity) {
-    Result<Issue> result = Result.of();
-    try {
-      result.set(issueService.setSeverity(issueKey, severity));
-    } catch (Exception e) {
-      result.addError(e.getMessage());
-    }
-    return result;
-  }
-
-  public Result<Issue> plan(String issueKey, @Nullable String actionPlanKey) {
-    Result<Issue> result = Result.of();
-    try {
-      result.set(issueService.plan(issueKey, actionPlanKey));
-    } catch (Exception e) {
-      result.addError(e.getMessage());
-    }
-    return result;
-  }
-
   public Result<IssueComment> addComment(String issueKey, String text) {
     Result<IssueComment> result = Result.of();
     try {
@@ -240,37 +167,6 @@ public class InternalRubyIssueService {
     return commentService.findComment(commentKey);
   }
 
-  /**
-   * Create manual issue
-   */
-  public Result<DefaultIssue> create(Map<String, String> params) {
-    Result<DefaultIssue> result = Result.of();
-    try {
-      // mandatory parameters
-      String componentKey = params.get("component");
-      if (StringUtils.isBlank(componentKey)) {
-        result.addError("Component is not set");
-      }
-      RuleKey ruleKey = null;
-      String rule = params.get("rule");
-      if (StringUtils.isBlank(rule)) {
-        result.addError(Result.Message.ofL10n("issue.manual.missing_rule"));
-      } else {
-        ruleKey = RuleKey.parse(rule);
-      }
-
-      if (result.ok()) {
-        DefaultIssue issue = issueService.createManualIssue(componentKey, ruleKey, RubyUtils.toInteger(params.get("line")), params.get("message"), params.get("severity"),
-          RubyUtils.toDouble(params.get("effortToFix")));
-        result.set(issue);
-      }
-
-    } catch (Exception e) {
-      result.addError(e.getMessage());
-    }
-    return result;
-  }
-
   public Collection<ActionPlan> findOpenActionPlans(String projectKey) {
     return actionPlanService.findOpenByProjectKey(projectKey, userSession);
   }
@@ -656,113 +552,6 @@ public class InternalRubyIssueService {
     return userSession.hasProjectPermissionByUuid(UserRole.ISSUE_ADMIN, projectUuid);
   }
 
-  /**
-   * Used by issue modification actions currently implemented in Rails
-   *
-   * @return the JSON representation of the modified issue, as a ready to use string
-   */
-  public String writeIssueJson(@Nullable Issue original) {
-    if (original == null) {
-      return "{}";
-    }
-
-    // Reloading from ES to avoid partial object, e.g for manual issues
-    Issue issue = issueService.getByKey(original.key());
-
-    StringWriter writer = new StringWriter();
-    JsonWriter json = JsonWriter.of(writer);
-    DbSession dbSession = dbClient.openSession(false);
-    try {
-      Map<String, User> usersByLogin = getIssueUsersByLogin(issue);
-
-      Set<String> componentUuids = ImmutableSet.of(issue.componentUuid());
-      Set<String> projectUuids = Sets.newHashSet();
-      Set<ComponentDto> componentDtos = Sets.newHashSet();
-      List<ComponentDto> projectDtos = Lists.newArrayList();
-
-      Map<String, ComponentDto> componentsByUuid = Maps.newHashMap();
-      Map<String, ComponentDto> projectsByComponentUuid = Maps.newHashMap();
-
-      List<ComponentDto> fileDtos = dbClient.componentDao().selectByUuids(dbSession, componentUuids);
-      List<ComponentDto> subProjectDtos = dbClient.componentDao().selectSubProjectsByComponentUuids(dbSession, componentUuids);
-      componentDtos.addAll(fileDtos);
-      componentDtos.addAll(subProjectDtos);
-      for (ComponentDto component : componentDtos) {
-        projectUuids.add(component.projectUuid());
-      }
-      projectDtos.addAll(dbClient.componentDao().selectByUuids(dbSession, projectUuids));
-      componentDtos.addAll(projectDtos);
-
-      for (ComponentDto componentDto : componentDtos) {
-        componentsByUuid.put(componentDto.uuid(), componentDto);
-      }
-
-      projectsByComponentUuid = issueComponentHelper.prepareComponentsAndProjects(projectUuids, componentUuids, componentsByUuid, componentDtos, subProjectDtos, dbSession);
-
-      Map<String, ActionPlan> actionPlans = newHashMap();
-      String actionPlanKey = issue.actionPlanKey();
-      if (actionPlanKey != null) {
-        actionPlans.put(actionPlanKey, actionPlanService.findByKey(actionPlanKey, userSession));
-      }
-
-      json.beginObject().name("issue");
-      issueWriter.write(json, issue,
-        usersByLogin,
-        componentsByUuid,
-        projectsByComponentUuid,
-        ImmutableMultimap.<String, DefaultIssueComment>of(),
-        actionPlans,
-        ISSUE_FIELDS);
-
-      json.name("users").beginArray();
-      String assignee = issue.assignee();
-      if (assignee != null && usersByLogin.containsKey(assignee)) {
-        userWriter.write(json, usersByLogin.get(assignee));
-      }
-      json.endArray();
-
-      json.name("projects").beginArray();
-      for (ComponentDto project : projectDtos) {
-        componentWriter.write(json, project, project);
-      }
-      json.endArray();
-
-      json.name("components").beginArray();
-      for (ComponentDto component : componentDtos) {
-        componentWriter.write(json, component, projectsByComponentUuid.get(component.uuid()));
-      }
-      json.endArray();
-
-      json.endObject().close();
-    } finally {
-      MyBatis.closeQuietly(dbSession);
-      IOUtils.closeQuietly(writer);
-    }
-    return writer.toString();
-  }
-
-  private Map<String, User> getIssueUsersByLogin(Issue issue) {
-    Map<String, User> usersByLogin = Maps.newHashMap();
-    String assignee = issue.assignee();
-    if (assignee != null) {
-      usersByLogin.put(assignee, userIndex.getByLogin(assignee));
-    }
-    String reporter = issue.reporter();
-    if (reporter != null) {
-      usersByLogin.put(reporter, userIndex.getByLogin(reporter));
-    }
-    return usersByLogin;
-  }
-
-  private enum ActionToKey implements Function<Action, String> {
-    INSTANCE;
-
-    @Override
-    public String apply(@Nonnull Action input) {
-      return input.key();
-    }
-  }
-
   private enum MatchIssueFilterParameters implements Predicate<Map.Entry<String, Object>> {
     INSTANCE;
 
index 9be3f1f3edfd9aa9fd63b4ffb636b60bb835d169..01a3f0aa4915a33dcb7016f3d30c57b05fc8247e 100644 (file)
@@ -26,6 +26,8 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.server.issue.IssueService;
 import org.sonar.server.user.UserSession;
 
+import static com.google.common.base.Strings.emptyToNull;
+
 public class AssignAction implements IssuesWsAction {
 
   public static final String ASSIGN_ACTION = "assign";
@@ -64,7 +66,7 @@ public class AssignAction implements IssuesWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    String assignee = request.param("assignee");
+    String assignee = emptyToNull(request.param("assignee"));
     if ("_me".equals(assignee) || BooleanUtils.isTrue(request.paramAsBoolean("me"))) {
       // Permission is currently checked by IssueService. We still
       // check that user is authenticated in order to get his login.
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/CreateAction.java
new file mode 100644 (file)
index 0000000..442c623
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.issue.IssueService;
+
+public class CreateAction implements IssuesWsAction {
+
+  public static final String ACTION = "create";
+
+  private final IssueService issueService;
+  private final OperationResponseWriter responseWriter;
+
+  public CreateAction(IssueService issueService, OperationResponseWriter responseWriter) {
+    this.issueService = issueService;
+    this.responseWriter = responseWriter;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction(ACTION)
+      .setDescription("Create a manual issue. Requires authentication and Browse permission on project")
+      .setSince("3.6")
+      .setHandler(this)
+      .setPost(true);
+
+    action.createParam("component")
+      .setDescription("Key of the component on which to log the issue")
+      .setRequired(true)
+      .setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
+    action.createParam("rule")
+      .setDescription("Manual rule key")
+      .setRequired(true)
+      .setExampleValue("manual:performance");
+    action.createParam("severity")
+      .setDescription("Severity of the issue")
+      .setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
+      .setPossibleValues(Severity.ALL);
+    action.createParam("line")
+      .setDescription("Line on which to log the issue. " +
+        "If no line is specified, the issue is attached to the component and not to a specific line")
+      .setExampleValue("15");
+    action.createParam("message")
+      .setDescription("Description of the issue")
+      .setExampleValue("blabla...");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    // required parameters
+    String componentKey = request.mandatoryParam("component");
+    RuleKey ruleKey = RuleKey.parse(request.mandatoryParam("rule"));
+
+    DefaultIssue issue = issueService.createManualIssue(componentKey, ruleKey,
+      request.paramAsInt("line"),
+      request.param("message"),
+      request.param("severity"),
+      null);
+
+    responseWriter.write(issue.key(), request, response);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java
new file mode 100644 (file)
index 0000000..ec5a977
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.issue.DefaultTransitions;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.issue.IssueService;
+
+public class DoTransitionAction implements IssuesWsAction {
+
+  public static final String ACTION = "do_transition";
+
+  private final IssueService issueService;
+  private final OperationResponseWriter responseWriter;
+
+  public DoTransitionAction(IssueService issueService, OperationResponseWriter responseWriter) {
+    this.issueService = issueService;
+    this.responseWriter = responseWriter;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction(ACTION)
+      .setDescription("Do workflow transition on an issue. Requires authentication and Browse permission on project")
+      .setSince("3.6")
+      .setHandler(this)
+      .setPost(true);
+
+    action.createParam("issue")
+      .setDescription("Key of the issue")
+      .setRequired(true)
+      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
+    action.createParam("transition")
+      .setDescription("Transition")
+      .setRequired(true)
+      .setPossibleValues(DefaultTransitions.ALL);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String key = request.mandatoryParam("issue");
+    issueService.doTransition(key, request.mandatoryParam("transition"));
+
+    responseWriter.write(key, request, response);
+  }
+}
index d9023ea75ae6c00dea1433713b1911a67858d227..8660724f7f158ec5365eade7499069977c20edf6 100644 (file)
 package org.sonar.server.issue.ws;
 
 import java.util.List;
-import org.sonar.api.server.ServerSide;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.action.Action;
+import org.sonar.api.server.ServerSide;
 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;
 
@@ -37,12 +35,10 @@ import static com.google.common.collect.Lists.newArrayList;
 public class IssueActionsWriter {
 
   private final IssueService issueService;
-  private final ActionService actionService;
   private final UserSession userSession;
 
-  public IssueActionsWriter(IssueService issueService, ActionService actionService, UserSession userSession) {
+  public IssueActionsWriter(IssueService issueService, UserSession userSession) {
     this.issueService = issueService;
-    this.actionService = actionService;
     this.userSession = userSession;
   }
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueComponentHelper.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueComponentHelper.java
deleted file mode 100644 (file)
index 7828aaf..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-
-import static com.google.common.collect.Maps.newHashMap;
-
-/**
- * This class computes some collections of {@link ComponentDto}s used to serialize issues.
- */
-public class IssueComponentHelper {
-
-  private final DbClient dbClient;
-
-  public IssueComponentHelper(DbClient dbClient) {
-    this.dbClient = dbClient;
-  }
-
-  public Map<String, ComponentDto> prepareComponentsAndProjects(Set<String> projectUuids, Set<String> componentUuids, Map<String, ComponentDto> componentsByUuid,
-                                                                Collection<ComponentDto> componentDtos, List<ComponentDto> projectDtos, DbSession session) {
-    Map<String, ComponentDto> projectsByComponentUuid;
-    List<ComponentDto> fileDtos = dbClient.componentDao().selectByUuids(session, componentUuids);
-    List<ComponentDto> subProjectDtos = dbClient.componentDao().selectSubProjectsByComponentUuids(session, componentUuids);
-    componentDtos.addAll(fileDtos);
-    componentDtos.addAll(subProjectDtos);
-    for (ComponentDto component : componentDtos) {
-      projectUuids.add(component.projectUuid());
-    }
-    projectDtos.addAll(dbClient.componentDao().selectByUuids(session, projectUuids));
-    componentDtos.addAll(projectDtos);
-
-    for (ComponentDto componentDto : componentDtos) {
-      componentsByUuid.put(componentDto.uuid(), componentDto);
-    }
-
-    projectsByComponentUuid = getProjectsByComponentUuid(componentDtos, projectDtos);
-    return projectsByComponentUuid;
-  }
-
-  private Map<String, ComponentDto> getProjectsByComponentUuid(Collection<ComponentDto> components, Collection<ComponentDto> projects) {
-    Map<String, ComponentDto> projectsByUuid = buildProjectsByUuid(projects);
-    return buildProjectsByComponentUuid(components, projectsByUuid);
-  }
-
-  private static Map<String, ComponentDto> buildProjectsByUuid(Collection<ComponentDto> projects) {
-    Map<String, ComponentDto> projectsByUuid = newHashMap();
-    for (ComponentDto project : projects) {
-      if (project == null) {
-        throw new IllegalStateException("Found a null project in issues");
-      }
-      if (project.uuid() == null) {
-        throw new IllegalStateException("Project has no UUID: " + project.getKey());
-      }
-      projectsByUuid.put(project.uuid(), project);
-    }
-    return projectsByUuid;
-  }
-
-  private static Map<String, ComponentDto> buildProjectsByComponentUuid(Collection<ComponentDto> components, Map<String, ComponentDto> projectsByUuid) {
-    Map<String, ComponentDto> projectsByComponentUuid = newHashMap();
-    for (ComponentDto component : components) {
-      if (component.uuid() == null) {
-        throw new IllegalStateException("Component has no UUID: " + component.getKey());
-      }
-      if (!projectsByUuid.containsKey(component.projectUuid())) {
-        throw new IllegalStateException("Project cannot be found for component: " + component.getKey() + " / " + component.uuid());
-      }
-      projectsByComponentUuid.put(component.uuid(), projectsByUuid.get(component.projectUuid()));
-    }
-    return projectsByComponentUuid;
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java
deleted file mode 100644 (file)
index 1bb99f9..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.IssueComment;
-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.db.component.ComponentDto;
-import org.sonar.core.issue.DefaultIssueComment;
-import org.sonar.markdown.Markdown;
-import org.sonar.server.user.UserSession;
-import org.sonar.server.ws.JsonWriterUtils;
-
-import static org.sonar.server.ws.JsonWriterUtils.writeIfNeeded;
-
-public class IssueJsonWriter {
-
-  private static final String FIELD_KEY = "key";
-  private static final String FIELD_COMPONENT = "component";
-  private static final String FIELD_COMPONENT_ID = "componentId";
-  private static final String FIELD_PROJECT = "project";
-  private static final String FIELD_SUB_PROJECT = "subProject";
-  private static final String FIELD_RULE = "rule";
-  private static final String FIELD_STATUS = "status";
-  private static final String FIELD_RESOLUTION = "resolution";
-  private static final String FIELD_AUTHOR = "author";
-  private static final String FIELD_REPORTER = "reporter";
-  private static final String FIELD_ASSIGNEE = "assignee";
-  private static final String FIELD_DEBT = "debt";
-  private static final String FIELD_LINE = "line";
-  private static final String FIELD_MESSAGE = "message";
-  private static final String FIELD_SEVERITY = "severity";
-  private static final String FIELD_ACTION_PLAN = "actionPlan";
-  private static final String FIELD_CREATION_DATE = "creationDate";
-  private static final String FIELD_UPDATE_DATE = "updateDate";
-  private static final String FIELD_CLOSE_DATE = "closeDate";
-  private static final String FIELD_TAGS = "tags";
-  private static final String FIELD_COMMENTS = "comments";
-  private static final String FIELD_ATTRIBUTES = "attr";
-  public static final String FIELD_ACTIONS = "actions";
-  public static final String FIELD_TRANSITIONS = "transitions";
-  public static final String FIELD_ACTION_PLAN_NAME = "actionPlanName";
-
-  public static final Set<String> EXTRA_FIELDS = ImmutableSet.of(
-    FIELD_ACTIONS, FIELD_TRANSITIONS, FIELD_ACTION_PLAN_NAME);
-
-  public static final Set<String> SELECTABLE_FIELDS = ImmutableSet.of(FIELD_COMPONENT, FIELD_PROJECT, FIELD_SUB_PROJECT, FIELD_RULE, FIELD_STATUS, FIELD_RESOLUTION, FIELD_AUTHOR,
-    FIELD_REPORTER, FIELD_ASSIGNEE, FIELD_DEBT, FIELD_LINE, FIELD_MESSAGE, FIELD_SEVERITY, FIELD_ACTION_PLAN, FIELD_CREATION_DATE, FIELD_UPDATE_DATE, FIELD_CLOSE_DATE,
-    FIELD_COMPONENT_ID, FIELD_TAGS, FIELD_COMMENTS, FIELD_ATTRIBUTES, FIELD_ACTIONS, FIELD_TRANSITIONS, FIELD_ACTION_PLAN_NAME);
-
-  private static final List<String> SELECTABLE_MINUS_EXTRAS = ImmutableList.copyOf(Sets.difference(SELECTABLE_FIELDS, EXTRA_FIELDS));
-
-  private final Durations durations;
-  private final UserSession userSession;
-  private final IssueActionsWriter actionsWriter;
-
-  public IssueJsonWriter(Durations durations, UserSession userSession, IssueActionsWriter actionsWriter) {
-    this.durations = durations;
-    this.userSession = userSession;
-    this.actionsWriter = actionsWriter;
-  }
-
-  public void write(JsonWriter json, Issue issue, Map<String, User> usersByLogin, Map<String, ComponentDto> componentsByUuid,
-    Map<String, ComponentDto> projectsByComponentUuid, Multimap<String, DefaultIssueComment> commentsByIssues, Map<String, ActionPlan> actionPlanByKeys,
-    @Nullable List<String> selectedFields) {
-
-    List<String> fields = useDefaultFieldsIfNotSpecified(selectedFields);
-
-    json.beginObject();
-
-    String actionPlanKey = issue.actionPlanKey();
-    ComponentDto file = componentsByUuid.get(issue.componentUuid());
-    ComponentDto project = null;
-    ComponentDto subProject = null;
-    if (file != null) {
-      project = projectsByComponentUuid.get(file.uuid());
-      if (!file.projectUuid().equals(file.moduleUuid())) {
-        subProject = componentsByUuid.get(file.moduleUuid());
-      }
-    }
-    Duration debt = issue.debt();
-    Date updateDate = issue.updateDate();
-
-    json.prop(FIELD_KEY, issue.key());
-    JsonWriterUtils.writeIfNeeded(json, file != null ? file.getKey() : null, FIELD_COMPONENT, fields);
-    // Only used for the compatibility with the Issues Java WS Client <= 4.4 used by Eclipse
-    writeIfNeeded(json, file != null ? file.getId() : null, FIELD_COMPONENT_ID, fields);
-    writeIfNeeded(json, project != null ? project.getKey() : null, FIELD_PROJECT, fields);
-    writeIfNeeded(json, subProject != null ? subProject.getKey() : null, FIELD_SUB_PROJECT, fields);
-    writeIfNeeded(json, issue.ruleKey().toString(), FIELD_RULE, fields);
-    writeIfNeeded(json, issue.status(), FIELD_STATUS, fields);
-    writeIfNeeded(json, issue.resolution(), FIELD_RESOLUTION, fields);
-    writeIfNeeded(json, issue.severity(), FIELD_SEVERITY, fields);
-    writeIfNeeded(json, issue.message(), FIELD_MESSAGE, fields);
-    writeIfNeeded(json, issue.line(), FIELD_LINE, fields);
-    writeIfNeeded(json, debt != null ? durations.encode(debt) : null, FIELD_DEBT, fields);
-    writeIfNeeded(json, issue.assignee(), FIELD_ASSIGNEE, fields);
-    writeIfNeeded(json, issue.reporter(), FIELD_REPORTER, fields);
-    writeIfNeeded(json, issue.authorLogin(), FIELD_AUTHOR, fields);
-    writeIfNeeded(json, actionPlanKey, FIELD_ACTION_PLAN, fields);
-    writeIfNeeded(json, isoDate(issue.creationDate()), FIELD_CREATION_DATE, fields);
-    writeIfNeeded(json, isoDate(updateDate), FIELD_UPDATE_DATE, fields);
-    writeIfNeeded(json, isoDate(issue.closeDate()), FIELD_CLOSE_DATE, fields);
-
-    if (JsonWriterUtils.isFieldNeeded(FIELD_TAGS, fields)) {
-      writeTags(issue, json);
-    }
-    if (JsonWriterUtils.isFieldNeeded(FIELD_COMMENTS, fields)) {
-      writeIssueComments(commentsByIssues.get(issue.key()), usersByLogin, json);
-    }
-    if (JsonWriterUtils.isFieldNeeded(FIELD_ATTRIBUTES, fields)) {
-      writeIssueAttributes(issue, json);
-    }
-    writeIssueExtraFields(issue, actionPlanByKeys, fields, json);
-    json.endObject();
-  }
-
-  private List<String> useDefaultFieldsIfNotSpecified(List<String> selectedFields) {
-    List<String> fields = Lists.newArrayList();
-    if (selectedFields == null || selectedFields.isEmpty()) {
-      fields.addAll(SELECTABLE_MINUS_EXTRAS);
-    } else {
-      fields.addAll(selectedFields);
-    }
-    return fields;
-  }
-
-  @CheckForNull
-  private static String isoDate(@Nullable Date date) {
-    if (date != null) {
-      return DateUtils.formatDateTime(date);
-    }
-    return null;
-  }
-
-  private static void writeTags(Issue issue, JsonWriter json) {
-    Collection<String> tags = issue.tags();
-    if (tags != null && !tags.isEmpty()) {
-      json.name(FIELD_TAGS).beginArray();
-      for (String tag : tags) {
-        json.value(tag);
-      }
-      json.endArray();
-    }
-  }
-
-  private void writeIssueComments(Collection<DefaultIssueComment> issueComments, Map<String, User> usersByLogin, JsonWriter json) {
-    if (!issueComments.isEmpty()) {
-      json.name(FIELD_COMMENTS).beginArray();
-      String login = userSession.getLogin();
-      for (IssueComment comment : issueComments) {
-        String userLogin = comment.userLogin();
-        User user = userLogin != null ? usersByLogin.get(userLogin) : null;
-        json.beginObject()
-          .prop("key", comment.key())
-          .prop("login", comment.userLogin())
-          .prop("email", user != null ? user.email() : null)
-          .prop("userName", user != null ? user.name() : null)
-          .prop("htmlText", Markdown.convertToHtml(comment.markdownText()))
-          .prop("markdown", comment.markdownText())
-          .prop("updatable", login != null && login.equals(userLogin))
-          .prop("createdAt", DateUtils.formatDateTime(comment.createdAt()))
-          .endObject();
-      }
-      json.endArray();
-    }
-  }
-
-  private static void writeIssueAttributes(Issue issue, JsonWriter json) {
-    if (!issue.attributes().isEmpty()) {
-      json.name(FIELD_ATTRIBUTES).beginObject();
-      for (Map.Entry<String, String> entry : issue.attributes().entrySet()) {
-        json.prop(entry.getKey(), entry.getValue());
-      }
-      json.endObject();
-    }
-  }
-
-  private void writeIssueExtraFields(Issue issue, Map<String, ActionPlan> actionPlanByKeys,
-    @Nullable List<String> fields, JsonWriter json) {
-    if (JsonWriterUtils.isFieldNeeded(FIELD_ACTIONS, fields)) {
-      actionsWriter.writeActions(issue, json);
-    }
-
-    if (JsonWriterUtils.isFieldNeeded(FIELD_TRANSITIONS, fields)) {
-      actionsWriter.writeTransitions(issue, json);
-    }
-
-    if (JsonWriterUtils.isFieldNeeded(FIELD_ACTION_PLAN_NAME, fields)) {
-      writeActionPlanName(issue, actionPlanByKeys, json);
-    }
-  }
-
-  private void writeActionPlanName(Issue issue, Map<String, ActionPlan> actionPlanByKeys, JsonWriter json) {
-    String actionPlanKey = issue.actionPlanKey();
-    if (actionPlanKey != null) {
-      ActionPlan actionPlan = actionPlanByKeys.get(actionPlanKey);
-      json.prop(FIELD_ACTION_PLAN_NAME, actionPlan != null ? actionPlan.name() : null);
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueWsModule.java
new file mode 100644 (file)
index 0000000..94fac58
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.core.platform.Module;
+
+public class IssueWsModule extends Module {
+  @Override
+  protected void configureModule() {
+    add(
+      IssuesWs.class,
+      SearchResponseLoader.class,
+      SearchResponseFormat.class,
+      OperationResponseWriter.class,
+      AssignAction.class,
+      CreateAction.class,
+      DoTransitionAction.class,
+      PlanAction.class,
+      ShowAction.class,
+      SearchAction.class,
+      SetSeverityAction.class,
+      TagsAction.class,
+      SetTagsAction.class,
+      ComponentTagsAction.class,
+      IssueActionsWriter.class,
+      AuthorsAction.class);
+  }
+}
index 3232d0725da156926a2422660a38903996f1698f..8ac665b2b7b9783afd6447c3b9c20263962c9886 100644 (file)
@@ -30,16 +30,10 @@ public class IssuesWs implements WebService {
   public static final String API_ENDPOINT = "api/issues";
 
   public static final String CHANGELOG_ACTION = "changelog";
-  public static final String ASSIGN_ACTION = "assign";
   public static final String ADD_COMMENT_ACTION = "add_comment";
   public static final String DELETE_COMMENT_ACTION = "delete_comment";
   public static final String EDIT_COMMENT_ACTION = "edit_comment";
-  public static final String SET_SEVERITY_ACTION = "set_severity";
-  public static final String PLAN_ACTION = "plan";
-  public static final String DO_TRANSITION_ACTION = "do_transition";
   public static final String TRANSITIONS_ACTION = "transitions";
-  public static final String CREATE_ACTION = "create";
-  public static final String DO_ACTION_ACTION = "do_action";
   public static final String BULK_CHANGE_ACTION = "bulk_change";
 
   private final IssuesWsAction[] actions;
@@ -65,12 +59,7 @@ public class IssuesWs implements WebService {
     defineAddCommentAction(controller);
     defineDeleteCommentAction(controller);
     defineEditCommentAction(controller);
-    defineSetSeverityAction(controller);
-    definePlanAction(controller);
-    defineDoTransitionAction(controller);
     defineTransitionsAction(controller);
-    defineCreateAction(controller);
-    defineDoActionAction(controller);
     defineBulkChangeAction(controller);
   }
 
@@ -135,59 +124,6 @@ public class IssuesWs implements WebService {
     RailsHandler.addFormatParam(action);
   }
 
-  private static void defineSetSeverityAction(NewController controller) {
-    WebService.NewAction action = controller.createAction(SET_SEVERITY_ACTION)
-      .setDescription("Change severity. Requires authentication and Browse permission on project")
-      .setSince("3.6")
-      .setHandler(RailsHandler.INSTANCE)
-      .setPost(true);
-
-    action.createParam("issue")
-      .setDescription("Key of the issue")
-      .setRequired(true)
-      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
-    action.createParam("severity")
-      .setDescription("New severity")
-      .setExampleValue(Severity.BLOCKER)
-      .setPossibleValues(Severity.ALL);
-    RailsHandler.addFormatParam(action);
-  }
-
-  private static void definePlanAction(NewController controller) {
-    WebService.NewAction action = controller.createAction(PLAN_ACTION)
-      .setDescription("Plan/Unplan an issue. Requires authentication and Browse permission on project")
-      .setSince("3.6")
-      .setHandler(RailsHandler.INSTANCE)
-      .setPost(true);
-
-    action.createParam("issue")
-      .setDescription("Key of the issue")
-      .setRequired(true)
-      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
-    action.createParam("plan")
-      .setDescription("Key of the action plan")
-      .setExampleValue("3f19de90-1521-4482-a737-a311758ff513");
-    RailsHandler.addFormatParam(action);
-  }
-
-  private static void defineDoTransitionAction(NewController controller) {
-    WebService.NewAction action = controller.createAction(DO_TRANSITION_ACTION)
-      .setDescription("Do workflow transition on an issue. Requires authentication and Browse permission on project")
-      .setSince("3.6")
-      .setHandler(RailsHandler.INSTANCE)
-      .setPost(true);
-
-    action.createParam("issue")
-      .setDescription("Key of the issue")
-      .setRequired(true)
-      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
-    action.createParam("transition")
-      .setDescription("Transition")
-      .setExampleValue("reopen")
-      .setPossibleValues(DefaultTransitions.ALL);
-    RailsHandler.addFormatParam(action);
-  }
-
   private static void defineTransitionsAction(NewController controller) {
     WebService.NewAction action = controller.createAction(TRANSITIONS_ACTION)
       .setDescription("Get Possible Workflow Transitions for an Issue. Requires Browse permission on project")
@@ -201,52 +137,6 @@ public class IssuesWs implements WebService {
       .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
   }
 
-  private static void defineCreateAction(NewController controller) {
-    WebService.NewAction action = controller.createAction(CREATE_ACTION)
-      .setDescription("Create a manual issue. Requires authentication and Browse permission on project")
-      .setSince("3.6")
-      .setHandler(RailsHandler.INSTANCE)
-      .setPost(true);
-
-    action.createParam("component")
-      .setDescription("Key of the component on which to log the issue")
-      .setRequired(true)
-      .setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
-    action.createParam("rule")
-      .setDescription("Manual rule key")
-      .setRequired(true)
-      .setExampleValue("manual:performance");
-    action.createParam("severity")
-      .setDescription("Severity of the issue")
-      .setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
-      .setPossibleValues(Severity.ALL);
-    action.createParam("line")
-      .setDescription("Line on which to log the issue. " +
-        "If no line is specified, the issue is attached to the component and not to a specific line")
-      .setExampleValue("15");
-    action.createParam("message")
-      .setDescription("Description of the issue")
-      .setExampleValue("blabla...");
-    RailsHandler.addFormatParam(action);
-  }
-
-  private static void defineDoActionAction(NewController controller) {
-    WebService.NewAction action = controller.createAction(DO_ACTION_ACTION)
-      .setDescription("Do workflow transition on an issue. Requires authentication and Browse permission on project")
-      .setSince("3.6")
-      .setHandler(RailsHandler.INSTANCE)
-      .setPost(true);
-
-    action.createParam("issue")
-      .setDescription("Key of the issue")
-      .setRequired(true)
-      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
-    action.createParam("actionKey")
-      .setDescription("Action to perform")
-      .setExampleValue("link-to-jira");
-    RailsHandler.addFormatParam(action);
-  }
-
   private static void defineBulkChangeAction(NewController controller) {
     WebService.NewAction action = controller.createAction(BULK_CHANGE_ACTION)
       .setDescription("Bulk change on issues. Requires authentication and User role on project(s)")
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/PlanAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/PlanAction.java
new file mode 100644 (file)
index 0000000..04d790e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.issue.IssueService;
+
+import static com.google.common.base.Strings.emptyToNull;
+
+public class PlanAction implements IssuesWsAction {
+
+  public static final String ACTION = "plan";
+
+  private final IssueService issueService;
+  private final OperationResponseWriter responseWriter;
+
+  public PlanAction(IssueService issueService, OperationResponseWriter responseWriter) {
+    this.issueService = issueService;
+    this.responseWriter = responseWriter;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction(ACTION)
+      .setDescription("Plan/Unplan an issue. Requires authentication and Browse permission on project")
+      .setSince("3.6")
+      .setHandler(this)
+      .setPost(true);
+
+    action.createParam("issue")
+      .setDescription("Key of the issue")
+      .setRequired(true)
+      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
+    action.createParam("plan")
+      .setDescription("Key of the action plan. Absent value removes the current plan.")
+      .setExampleValue("3f19de90-1521-4482-a737-a311758ff513");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String key = request.mandatoryParam("issue");
+    issueService.plan(key, emptyToNull(request.param("plan")));
+
+    responseWriter.write(key, request, response);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/Search2Action.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/Search2Action.java
deleted file mode 100644 (file)
index e0f5f66..0000000
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * 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.base.Function;
-import com.google.common.collect.Lists;
-import com.google.common.io.Resources;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.Nullable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.rule.Severity;
-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.server.ws.WebService.Param;
-import org.sonar.api.utils.Paging;
-import org.sonar.core.rule.RuleKeyFunctions;
-import org.sonar.server.es.Facets;
-import org.sonar.server.es.SearchOptions;
-import org.sonar.server.es.SearchResult;
-import org.sonar.server.issue.IssueQuery;
-import org.sonar.server.issue.IssueQueryService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.issue.filter.IssueFilterParameters;
-import org.sonar.server.issue.index.IssueDoc;
-import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.user.UserSession;
-import org.sonar.server.ws.WsUtils;
-import org.sonarqube.ws.Issues;
-
-import static com.google.common.collect.FluentIterable.from;
-import static com.google.common.collect.Iterables.concat;
-import static java.util.Collections.singletonList;
-
-public class Search2Action implements IssuesWsAction {
-
-  private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
-  public static final String ADDITIONAL_FIELDS = "additionalFields";
-  public static final String SEARCH_ACTION = "search2";
-
-  private final UserSession userSession;
-  private final IssueService service;
-  private final IssueQueryService issueQueryService;
-  private final SearchResponseLoader searchResponseLoader;
-  private final SearchResponseFormat searchResponseFormat;
-
-  public Search2Action(UserSession userSession, IssueService service, IssueQueryService issueQueryService,
-    SearchResponseLoader searchResponseLoader, SearchResponseFormat searchResponseFormat) {
-    this.userSession = userSession;
-    this.service = service;
-    this.issueQueryService = issueQueryService;
-    this.searchResponseLoader = searchResponseLoader;
-    this.searchResponseFormat = searchResponseFormat;
-  }
-
-  @Override
-  public void define(WebService.NewController controller) {
-    WebService.NewAction action = controller
-      .createAction(SEARCH_ACTION)
-      .setHandler(this)
-      .setDescription(
-        "Search for issues. Requires Browse permission on project(s)")
-      .setSince("3.6")
-      .setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
-
-    action.addPagingParams(100);
-    action.createParam(Param.FACETS)
-      .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
-      .setPossibleValues(IssueIndex.SUPPORTED_FACETS);
-    action.createParam(IssueFilterParameters.FACET_MODE)
-      .setDefaultValue(IssueFilterParameters.FACET_MODE_COUNT)
-      .setDescription("Choose the returned value for facet items, either count of issues or sum of debt.")
-      .setPossibleValues(IssueFilterParameters.FACET_MODE_COUNT, IssueFilterParameters.FACET_MODE_DEBT);
-    action.addSortParams(IssueQuery.SORTS, null, true);
-    action.createParam(ADDITIONAL_FIELDS)
-      .setDescription("Comma-separated list of the optional fields to be returned in response.")
-      .setPossibleValues(SearchAdditionalField.possibleValues());
-    addComponentRelatedParams(action);
-    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.RULES)
-      .setDescription("Comma-separated list of coding rule keys. Format is <repository>:<rule>")
-      .setExampleValue("squid:AvoidCycles");
-    action.createParam(IssueFilterParameters.TAGS)
-      .setDescription("Comma-separated list of tags.")
-      .setExampleValue("security,convention");
-    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.AUTHORS)
-      .setDescription("Comma-separated list of SCM accounts")
-      .setExampleValue("torvalds@linux-foundation.org");
-    action.createParam(IssueFilterParameters.ASSIGNEES)
-      .setDescription("Comma-separated list of assignee logins. The value '__me__' can be used as a placeholder for user who performs the request")
-      .setExampleValue("admin,usera,__me__");
-    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(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 (exclusive). Format: date or datetime ISO formats. If this parameter is set, createdSince must not be set")
-      .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.CREATED_IN_LAST)
-      .setDescription("To retrieve issues created during a time span before the current time (exclusive). " +
-        "Accepted units are 'y' for year, 'm' for month, 'w' for week and 'd' for day. " +
-        "If this parameter is set, createdAfter must not be set")
-      .setExampleValue("1m2w (1 month 2 weeks)");
-  }
-
-  private static void addComponentRelatedParams(WebService.NewAction action) {
-    action.createParam(IssueFilterParameters.ON_COMPONENT_ONLY)
-      .setDescription("Return only issues at a component's level, not on its descendants (modules, directories, files, etc). " +
-        "This parameter is only considered when componentKeys or componentUuids is set. " +
-        "Using the deprecated componentRoots or componentRootUuids parameters will set this parameter to false. " +
-        "Using the deprecated components parameter will set this parameter to true.")
-      .setBooleanPossibleValues()
-      .setDefaultValue("false");
-
-    action.createParam(IssueFilterParameters.COMPONENT_KEYS)
-      .setDescription("To retrieve issues associated to a specific list of components sub-components (comma-separated list of component keys). " +
-        "A component can be a view, developer, project, module, directory or file. " +
-        "If this parameter is set, componentUuids must not be set.")
-      .setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
-    action.createParam(IssueFilterParameters.COMPONENTS)
-      .setDescription("Deprecated since 5.1. If used, will have the same meaning as componentKeys AND onComponentOnly=true.");
-    action.createParam(IssueFilterParameters.COMPONENT_UUIDS)
-      .setDescription("To retrieve issues associated to a specific list of components their sub-components (comma-separated list of component UUIDs). " +
-        INTERNAL_PARAMETER_DISCLAIMER +
-        "A component can be a project, module, directory or file. " +
-        "If this parameter is set, componentKeys must not be set.")
-      .setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2");
-
-    action.createParam(IssueFilterParameters.COMPONENT_ROOTS)
-      .setDescription("Deprecated since 5.1. If used, will have the same meaning as componentKeys AND onComponentOnly=false.");
-    action.createParam(IssueFilterParameters.COMPONENT_ROOT_UUIDS)
-      .setDescription("Deprecated since 5.1. If used, will have the same meaning as componentUuids AND onComponentOnly=false.");
-
-    action.createParam(IssueFilterParameters.PROJECTS)
-      .setDescription("Deprecated since 5.1. See projectKeys");
-    action.createParam(IssueFilterParameters.PROJECT_KEYS)
-      .setDescription("To retrieve issues associated to a specific list of projects (comma-separated list of project keys). " +
-        INTERNAL_PARAMETER_DISCLAIMER +
-        "If this parameter is set, projectUuids must not be set.")
-      .setDeprecatedKey(IssueFilterParameters.PROJECTS)
-      .setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
-    action.createParam(IssueFilterParameters.PROJECT_UUIDS)
-      .setDescription("To retrieve issues associated to a specific list of projects (comma-separated list of project UUIDs). " +
-        INTERNAL_PARAMETER_DISCLAIMER +
-        "Views are not supported. If this parameter is set, projectKeys must not be set.")
-      .setExampleValue("7d8749e8-3070-4903-9188-bdd82933bb92");
-
-    action.createParam(IssueFilterParameters.MODULE_UUIDS)
-      .setDescription("To retrieve issues associated to a specific list of modules (comma-separated list of module UUIDs). " +
-        INTERNAL_PARAMETER_DISCLAIMER +
-        "Views are not supported. If this parameter is set, moduleKeys must not be set.")
-      .setExampleValue("7d8749e8-3070-4903-9188-bdd82933bb92");
-
-    action.createParam(IssueFilterParameters.DIRECTORIES)
-      .setDescription("Since 5.1. To retrieve issues associated to a specific list of directories (comma-separated list of directory paths). " +
-        "This parameter is only meaningful when a module is selected. " +
-        INTERNAL_PARAMETER_DISCLAIMER)
-      .setExampleValue("src/main/java/org/sonar/server/");
-
-    action.createParam(IssueFilterParameters.FILE_UUIDS)
-      .setDescription("To retrieve issues associated to a specific list of files (comma-separated list of file UUIDs). " +
-        INTERNAL_PARAMETER_DISCLAIMER)
-      .setExampleValue("bdd82933-3070-4903-9188-7d8749e8bb92");
-  }
-
-  @Override
-  public final void handle(Request request, Response response) throws Exception {
-    // prepare the Elasticsearch request
-    SearchOptions options = new SearchOptions();
-    options.setPage(request.mandatoryParamAsInt(Param.PAGE), request.mandatoryParamAsInt(Param.PAGE_SIZE));
-    options.addFacets(request.paramAsStrings(Param.FACETS));
-    EnumSet<SearchAdditionalField> additionalFields = SearchAdditionalField.getFromRequest(request);
-    IssueQuery query = issueQueryService.createFromRequest(request);
-
-    // execute request
-    SearchResult<IssueDoc> result = service.search(query, options);
-    List<String> issueKeys = from(result.getDocs()).transform(IssueDocToKey.INSTANCE).toList();
-
-    // load the additional information to be returned in response
-    SearchResponseLoader.Collector collector = new SearchResponseLoader.Collector(additionalFields, issueKeys);
-    collectLoggedInUser(collector);
-    collectRequestParams(collector, request);
-    Facets facets = null;
-    if (!options.getFacets().isEmpty()) {
-      facets = result.getFacets();
-      // add missing values to facets. For example if assignee "john" and facet on "assignees" are requested, then
-      // "john" should always be listed in the facet. If it is not present, then it is added with value zero.
-      // This is a constraint from webapp UX.
-      completeFacets(facets, options, request);
-      collectFacets(collector, facets);
-    }
-    SearchResponseData data = searchResponseLoader.load(collector, facets);
-
-    // format response
-
-    // Filter and reorder facets according to the requested ordered names.
-    // Must be done after loading of data as the "hidden" facet "debt"
-    // can be used to get total debt.
-    facets = reorderFacets(facets, options.getFacets());
-
-    // FIXME allow long in Paging
-    Paging paging = Paging.create(options.getLimit(), options.getPage(), (int) result.getTotal());
-    Issues.Search responseBody = searchResponseFormat.formatSearch(additionalFields, data, paging, facets);
-    WsUtils.writeProtobuf(responseBody, request, response);
-  }
-
-  private Facets reorderFacets(@Nullable Facets facets, Collection<String> orderedNames) {
-    if (facets == null) {
-      return null;
-    }
-    LinkedHashMap<String, LinkedHashMap<String, Long>> orderedFacets = new LinkedHashMap<>();
-    for (String facetName : orderedNames) {
-      LinkedHashMap<String, Long> facet = facets.get(facetName);
-      if (facet != null) {
-        orderedFacets.put(facetName, facet);
-      }
-    }
-    return new Facets(orderedFacets);
-  }
-
-  private void completeFacets(Facets facets, SearchOptions options, Request request) {
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.SEVERITIES, Severity.ALL);
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.STATUSES, Issue.STATUSES);
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.RESOLUTIONS, concat(singletonList(""), Issue.RESOLUTIONS));
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.PROJECT_UUIDS, request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));
-
-    List<String> assignees = Lists.newArrayList("");
-    List<String> assigneesFromRequest = request.paramAsStrings(IssueFilterParameters.ASSIGNEES);
-    if (assigneesFromRequest != null) {
-      assignees.addAll(assigneesFromRequest);
-      assignees.remove(IssueQueryService.LOGIN_MYSELF);
-    }
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.ASSIGNEES, assignees);
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.FACET_ASSIGNED_TO_ME, singletonList(userSession.getLogin()));
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.REPORTERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.RULES, request.paramAsStrings(IssueFilterParameters.RULES));
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.LANGUAGES, request.paramAsStrings(IssueFilterParameters.LANGUAGES));
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.TAGS, request.paramAsStrings(IssueFilterParameters.TAGS));
-    List<String> actionPlans = Lists.newArrayList("");
-    List<String> actionPlansFromRequest = request.paramAsStrings(IssueFilterParameters.ACTION_PLANS);
-    if (actionPlansFromRequest != null) {
-      actionPlans.addAll(actionPlansFromRequest);
-    }
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.ACTION_PLANS, actionPlans);
-    addMandatoryValuesToFacet(facets, IssueFilterParameters.COMPONENT_UUIDS, request.paramAsStrings(IssueFilterParameters.COMPONENT_UUIDS));
-
-    for (String facetName : options.getFacets()) {
-      LinkedHashMap<String, Long> buckets = facets.get(facetName);
-      if (!IssueFilterParameters.FACET_ASSIGNED_TO_ME.equals(facetName)) {
-        if (buckets != null) {
-          List<String> requestParams = request.paramAsStrings(facetName);
-          if (requestParams != null) {
-            for (String param : requestParams) {
-              if (!buckets.containsKey(param) && !IssueQueryService.LOGIN_MYSELF.equals(param)) {
-                // Prevent appearance of a glitch value due to dedicated parameter for this facet
-                buckets.put(param, 0L);
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  private void addMandatoryValuesToFacet(Facets facets, String facetName, @Nullable Iterable<String> mandatoryValues) {
-    Map<String, Long> buckets = facets.get(facetName);
-    if (buckets != null && mandatoryValues != null) {
-      for (String mandatoryValue : mandatoryValues) {
-        if (!buckets.containsKey(mandatoryValue)) {
-          buckets.put(mandatoryValue, 0L);
-        }
-      }
-    }
-  }
-
-  private void collectLoggedInUser(SearchResponseLoader.Collector collector) {
-    if (userSession.isLoggedIn()) {
-      collector.add(SearchAdditionalField.USERS, userSession.getLogin());
-    }
-  }
-
-  private void collectFacets(SearchResponseLoader.Collector collector, Facets facets) {
-    Set<String> facetRules = facets.getBucketKeys(IssueFilterParameters.RULES);
-    if (facetRules != null) {
-      collector.addAll(SearchAdditionalField.RULES, from(facetRules).transform(RuleKeyFunctions.stringToRuleKey()));
-    }
-    collector.addProjectUuids(facets.getBucketKeys(IssueFilterParameters.PROJECT_UUIDS));
-    collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.COMPONENT_UUIDS));
-    collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.FILE_UUIDS));
-    collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.MODULE_UUIDS));
-    collector.addAll(SearchAdditionalField.USERS, facets.getBucketKeys(IssueFilterParameters.ASSIGNEES));
-    collector.addAll(SearchAdditionalField.USERS, facets.getBucketKeys(IssueFilterParameters.REPORTERS));
-    collector.addAll(SearchAdditionalField.ACTION_PLANS, facets.getBucketKeys(IssueFilterParameters.ACTION_PLANS));
-  }
-
-  private void collectRequestParams(SearchResponseLoader.Collector collector, Request request) {
-    collector.addProjectUuids(request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));
-    collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.FILE_UUIDS));
-    collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.MODULE_UUIDS));
-    collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOT_UUIDS));
-    collector.addAll(SearchAdditionalField.USERS, request.paramAsStrings(IssueFilterParameters.ASSIGNEES));
-    collector.addAll(SearchAdditionalField.USERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
-    collector.addAll(SearchAdditionalField.ACTION_PLANS, request.paramAsStrings(IssueFilterParameters.ACTION_PLANS));
-  }
-
-  private enum IssueDocToKey implements Function<IssueDoc, String> {
-    INSTANCE;
-    @Override
-    public String apply(IssueDoc input) {
-      return input.key();
-    }
-  }
-}
index d9729d25d67dc23b541b049b41121ecfcec5c2a3..f0ce7bf9d1b7b4e7cbaaadbdd2b4125801cb71b9 100644 (file)
  */
 package org.sonar.server.issue.ws;
 
-import com.google.common.collect.ArrayListMultimap;
+import com.google.common.base.Function;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
 import com.google.common.io.Resources;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
+import java.util.EnumSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import org.apache.commons.lang.BooleanUtils;
-import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.ActionPlan;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.resources.Language;
-import org.sonar.api.resources.Languages;
-import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 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.server.ws.WebService.Param;
-import org.sonar.api.user.User;
-import org.sonar.api.user.UserFinder;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.core.issue.DefaultIssueComment;
-import org.sonar.db.DbSession;
-import org.sonar.server.component.ws.ComponentJsonWriter;
-import org.sonar.db.DbClient;
+import org.sonar.api.utils.Paging;
+import org.sonar.core.rule.RuleKeyFunctions;
 import org.sonar.server.es.Facets;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.es.SearchResult;
 import org.sonar.server.issue.IssueQuery;
 import org.sonar.server.issue.IssueQueryService;
 import org.sonar.server.issue.IssueService;
-import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.filter.IssueFilterParameters;
 import org.sonar.server.issue.index.IssueDoc;
 import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.rule.Rule;
-import org.sonar.server.rule.RuleService;
 import org.sonar.server.user.UserSession;
-import org.sonar.server.user.ws.UserJsonWriter;
+import org.sonar.server.ws.WsUtils;
+import org.sonarqube.ws.Issues;
 
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-import static com.google.common.collect.Sets.newHashSet;
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Iterables.concat;
+import static java.util.Collections.singletonList;
 
 public class SearchAction implements IssuesWsAction {
 
-  public static final String SEARCH_ACTION = "search";
-
   private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
+  public static final String ADDITIONAL_FIELDS = "additionalFields";
+  public static final String SEARCH_ACTION = "search";
 
+  private final UserSession userSession;
   private final IssueService service;
-
   private final IssueQueryService issueQueryService;
-  private final RuleService ruleService;
-  private final DbClient dbClient;
-  private final ActionPlanService actionPlanService;
-  private final UserFinder userFinder;
-  private final I18n i18n;
-  private final Languages languages;
-  private final UserSession userSession;
-  private final IssueJsonWriter issueWriter;
-  private final UserJsonWriter userWriter;
-  private final IssueComponentHelper issueComponentHelper;
-  private final ComponentJsonWriter componentWriter;
+  private final SearchResponseLoader searchResponseLoader;
+  private final SearchResponseFormat searchResponseFormat;
 
-  public SearchAction(DbClient dbClient, IssueService service, IssueQueryService issueQueryService,
-    RuleService ruleService, ActionPlanService actionPlanService, UserFinder userFinder, I18n i18n, Languages languages,
-    UserSession userSession, IssueJsonWriter issueWriter, IssueComponentHelper issueComponentHelper, ComponentJsonWriter componentWriter, UserJsonWriter userWriter) {
-    this.dbClient = dbClient;
+  public SearchAction(UserSession userSession, IssueService service, IssueQueryService issueQueryService,
+                      SearchResponseLoader searchResponseLoader, SearchResponseFormat searchResponseFormat) {
+    this.userSession = userSession;
     this.service = service;
     this.issueQueryService = issueQueryService;
-    this.ruleService = ruleService;
-    this.actionPlanService = actionPlanService;
-    this.userFinder = userFinder;
-    this.i18n = i18n;
-    this.languages = languages;
-    this.userSession = userSession;
-    this.issueWriter = issueWriter;
-    this.userWriter = userWriter;
-    this.issueComponentHelper = issueComponentHelper;
-    this.componentWriter = componentWriter;
+    this.searchResponseLoader = searchResponseLoader;
+    this.searchResponseFormat = searchResponseFormat;
   }
 
   @Override
@@ -119,12 +81,12 @@ public class SearchAction implements IssuesWsAction {
       .createAction(SEARCH_ACTION)
       .setHandler(this)
       .setDescription(
-        "Get a list of issues. Requires Browse permission on project(s)")
+        "Search for issues. Requires Browse permission on project(s)")
       .setSince("3.6")
       .setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
 
     action.addPagingParams(100);
-    action.createParam(WebService.Param.FACETS)
+    action.createParam(Param.FACETS)
       .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
       .setPossibleValues(IssueIndex.SUPPORTED_FACETS);
     action.createParam(IssueFilterParameters.FACET_MODE)
@@ -132,8 +94,9 @@ public class SearchAction implements IssuesWsAction {
       .setDescription("Choose the returned value for facet items, either count of issues or sum of debt.")
       .setPossibleValues(IssueFilterParameters.FACET_MODE_COUNT, IssueFilterParameters.FACET_MODE_DEBT);
     action.addSortParams(IssueQuery.SORTS, null, true);
-    action.addFieldsParam(IssueJsonWriter.SELECTABLE_FIELDS);
-
+    action.createParam(ADDITIONAL_FIELDS)
+      .setDescription("Comma-separated list of the optional fields to be returned in response.")
+      .setPossibleValues(SearchAdditionalField.possibleValues());
     addComponentRelatedParams(action);
     action.createParam(IssueFilterParameters.ISSUES)
       .setDescription("Comma-separated list of issue keys")
@@ -159,14 +122,6 @@ public class SearchAction implements IssuesWsAction {
     action.createParam(IssueFilterParameters.TAGS)
       .setDescription("Comma-separated list of tags.")
       .setExampleValue("security,convention");
-    action.createParam(IssueFilterParameters.HIDE_RULES)
-      .setDescription("To not return rules")
-      .setDefaultValue(false)
-      .setBooleanPossibleValues();
-    action.createParam(IssueFilterParameters.HIDE_COMMENTS)
-      .setDescription("To not return comments")
-      .setDefaultValue(false)
-      .setBooleanPossibleValues();
     action.createParam(IssueFilterParameters.ACTION_PLANS)
       .setDescription("Comma-separated list of action plan keys (not names)")
       .setExampleValue("3f19de90-1521-4482-a737-a311758ff513");
@@ -202,8 +157,6 @@ public class SearchAction implements IssuesWsAction {
         "Accepted units are 'y' for year, 'm' for month, 'w' for week and 'd' for day. " +
         "If this parameter is set, createdAfter must not be set")
       .setExampleValue("1m2w (1 month 2 weeks)");
-    action.createParam("format")
-      .setDescription("Only json format is available. This parameter is kept only for backward compatibility and shouldn't be used anymore");
   }
 
   private static void addComponentRelatedParams(WebService.NewAction action) {
@@ -268,116 +221,64 @@ public class SearchAction implements IssuesWsAction {
 
   @Override
   public final void handle(Request request, Response response) throws Exception {
+    // prepare the Elasticsearch request
     SearchOptions options = new SearchOptions();
-    options.setPage(request.mandatoryParamAsInt(WebService.Param.PAGE), request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE));
-    options.addFacets(request.paramAsStrings(WebService.Param.FACETS));
-
+    options.setPage(request.mandatoryParamAsInt(Param.PAGE), request.mandatoryParamAsInt(Param.PAGE_SIZE));
+    options.addFacets(request.paramAsStrings(Param.FACETS));
+    EnumSet<SearchAdditionalField> additionalFields = SearchAdditionalField.getFromRequest(request);
     IssueQuery query = issueQueryService.createFromRequest(request);
-    SearchResult<IssueDoc> result = service.search(query, options);
 
-    JsonWriter json = response.newJsonWriter().beginObject();
-    options.writeJson(json, result.getTotal());
-    options.writeDeprecatedJson(json, result.getTotal());
+    // execute request
+    SearchResult<IssueDoc> result = service.search(query, options);
+    List<String> issueKeys = from(result.getDocs()).transform(IssueDocToKey.INSTANCE).toList();
 
-    writeResponse(request, result, json);
+    // load the additional information to be returned in response
+    SearchResponseLoader.Collector collector = new SearchResponseLoader.Collector(additionalFields, issueKeys);
+    collectLoggedInUser(collector);
+    collectRequestParams(collector, request);
+    Facets facets = null;
     if (!options.getFacets().isEmpty()) {
-      writeFacets(request, options, result, json);
-    }
-    json.endObject().close();
-  }
-
-  private void writeResponse(Request request, SearchResult<IssueDoc> result, JsonWriter json) {
-    Map<String, Long> debtFacet = result.getFacets().get(IssueIndex.DEBT_AGGREGATION_NAME);
-    if (debtFacet != null) {
-      json.prop("debtTotal", debtFacet.get(Facets.TOTAL));
-    }
-
-    List<String> issueKeys = newArrayList();
-    Set<RuleKey> ruleKeys = newHashSet();
-    Set<String> projectUuids = newHashSet();
-    Set<String> componentUuids = newHashSet();
-    Set<String> actionPlanKeys = newHashSet();
-    List<String> userLogins = newArrayList();
-    Map<String, User> usersByLogin = newHashMap();
-    Map<String, ComponentDto> componentsByUuid = newHashMap();
-    Multimap<String, DefaultIssueComment> commentsByIssues = ArrayListMultimap.create();
-    Collection<ComponentDto> componentDtos = newHashSet();
-    List<ComponentDto> projectDtos = Lists.newArrayList();
-    Map<String, ComponentDto> projectsByComponentUuid = newHashMap();
-
-    for (IssueDoc issueDoc : result.getDocs()) {
-      issueKeys.add(issueDoc.key());
-      ruleKeys.add(issueDoc.ruleKey());
-      projectUuids.add(issueDoc.projectUuid());
-      componentUuids.add(issueDoc.componentUuid());
-      actionPlanKeys.add(issueDoc.actionPlanKey());
-      if (issueDoc.reporter() != null) {
-        userLogins.add(issueDoc.reporter());
-      }
-      if (issueDoc.assignee() != null) {
-        userLogins.add(issueDoc.assignee());
-      }
+      facets = result.getFacets();
+      // add missing values to facets. For example if assignee "john" and facet on "assignees" are requested, then
+      // "john" should always be listed in the facet. If it is not present, then it is added with value zero.
+      // This is a constraint from webapp UX.
+      completeFacets(facets, options, request);
+      collectFacets(collector, facets);
     }
+    SearchResponseData data = searchResponseLoader.load(collector, facets);
 
-    collectRuleKeys(request, result, ruleKeys);
+    // format response
 
-    collectFacetsData(request, result, projectUuids, componentUuids, userLogins, actionPlanKeys);
+    // Filter and reorder facets according to the requested ordered names.
+    // Must be done after loading of data as the "hidden" facet "debt"
+    // can be used to get total debt.
+    facets = reorderFacets(facets, options.getFacets());
 
-    if (userSession.isLoggedIn()) {
-      userLogins.add(userSession.getLogin());
-    }
-
-    DbSession session = dbClient.openSession(false);
-    try {
-      if (!BooleanUtils.isTrue(request.paramAsBoolean(IssueFilterParameters.HIDE_COMMENTS))) {
-        List<DefaultIssueComment> comments = dbClient.issueChangeDao().selectCommentsByIssues(session, issueKeys);
-        for (DefaultIssueComment issueComment : comments) {
-          userLogins.add(issueComment.userLogin());
-          commentsByIssues.put(issueComment.issueKey(), issueComment);
-        }
-      }
-      usersByLogin = getUsersByLogin(userLogins);
-
-      projectsByComponentUuid = issueComponentHelper.prepareComponentsAndProjects(projectUuids, componentUuids, componentsByUuid, componentDtos, projectDtos, session);
-
-      writeProjects(json, projectDtos);
-      writeComponents(json, componentDtos, projectsByComponentUuid);
-    } finally {
-      session.close();
-    }
-
-    Map<String, ActionPlan> actionPlanByKeys = getActionPlanByKeys(actionPlanKeys);
-
-    writeIssues(result, commentsByIssues, usersByLogin, actionPlanByKeys, componentsByUuid, projectsByComponentUuid,
-      request.paramAsStrings(Param.FIELDS), json);
-    writeRules(json, !request.mandatoryParamAsBoolean(IssueFilterParameters.HIDE_RULES) ? ruleService.getByKeys(ruleKeys) : Collections.<Rule>emptyList());
-    writeUsers(json, usersByLogin);
-    writeActionPlans(json, actionPlanByKeys.values());
-    writeLanguages(json);
+    // FIXME allow long in Paging
+    Paging paging = Paging.create(options.getLimit(), options.getPage(), (int) result.getTotal());
+    Issues.Search responseBody = searchResponseFormat.formatSearch(additionalFields, data, paging, facets);
+    WsUtils.writeProtobuf(responseBody, request, response);
   }
 
-  private static void collectRuleKeys(Request request, SearchResult<IssueDoc> result, Set<RuleKey> ruleKeys) {
-    Set<String> facetRules = result.getFacets().getBucketKeys(IssueFilterParameters.RULES);
-    if (facetRules != null) {
-      for (String rule : facetRules) {
-        ruleKeys.add(RuleKey.parse(rule));
-      }
+  private Facets reorderFacets(@Nullable Facets facets, Collection<String> orderedNames) {
+    if (facets == null) {
+      return null;
     }
-    List<String> rulesFromRequest = request.paramAsStrings(IssueFilterParameters.RULES);
-    if (rulesFromRequest != null) {
-      for (String ruleKey : rulesFromRequest) {
-        ruleKeys.add(RuleKey.parse(ruleKey));
+    LinkedHashMap<String, LinkedHashMap<String, Long>> orderedFacets = new LinkedHashMap<>();
+    for (String facetName : orderedNames) {
+      LinkedHashMap<String, Long> facet = facets.get(facetName);
+      if (facet != null) {
+        orderedFacets.put(facetName, facet);
       }
     }
+    return new Facets(orderedFacets);
   }
 
-  protected void writeFacets(Request request, SearchOptions options, SearchResult<IssueDoc> results, JsonWriter json) {
-    addMandatoryFacetValues(results, IssueFilterParameters.SEVERITIES, Severity.ALL);
-    addMandatoryFacetValues(results, IssueFilterParameters.STATUSES, Issue.STATUSES);
-    List<String> resolutions = Lists.newArrayList("");
-    resolutions.addAll(Issue.RESOLUTIONS);
-    addMandatoryFacetValues(results, IssueFilterParameters.RESOLUTIONS, resolutions);
-    addMandatoryFacetValues(results, IssueFilterParameters.PROJECT_UUIDS, request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));
+  private void completeFacets(Facets facets, SearchOptions options, Request request) {
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.SEVERITIES, Severity.ALL);
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.STATUSES, Issue.STATUSES);
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.RESOLUTIONS, concat(singletonList(""), Issue.RESOLUTIONS));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.PROJECT_UUIDS, request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));
 
     List<String> assignees = Lists.newArrayList("");
     List<String> assigneesFromRequest = request.paramAsStrings(IssueFilterParameters.ASSIGNEES);
@@ -385,229 +286,84 @@ public class SearchAction implements IssuesWsAction {
       assignees.addAll(assigneesFromRequest);
       assignees.remove(IssueQueryService.LOGIN_MYSELF);
     }
-    addMandatoryFacetValues(results, IssueFilterParameters.ASSIGNEES, assignees);
-    addMandatoryFacetValues(results, IssueFilterParameters.FACET_ASSIGNED_TO_ME, Arrays.asList(userSession.getLogin()));
-    addMandatoryFacetValues(results, IssueFilterParameters.REPORTERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
-    addMandatoryFacetValues(results, IssueFilterParameters.RULES, request.paramAsStrings(IssueFilterParameters.RULES));
-    addMandatoryFacetValues(results, IssueFilterParameters.LANGUAGES, request.paramAsStrings(IssueFilterParameters.LANGUAGES));
-    addMandatoryFacetValues(results, IssueFilterParameters.TAGS, request.paramAsStrings(IssueFilterParameters.TAGS));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.ASSIGNEES, assignees);
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.FACET_ASSIGNED_TO_ME, singletonList(userSession.getLogin()));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.REPORTERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.RULES, request.paramAsStrings(IssueFilterParameters.RULES));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.LANGUAGES, request.paramAsStrings(IssueFilterParameters.LANGUAGES));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.TAGS, request.paramAsStrings(IssueFilterParameters.TAGS));
     List<String> actionPlans = Lists.newArrayList("");
     List<String> actionPlansFromRequest = request.paramAsStrings(IssueFilterParameters.ACTION_PLANS);
     if (actionPlansFromRequest != null) {
       actionPlans.addAll(actionPlansFromRequest);
     }
-    addMandatoryFacetValues(results, IssueFilterParameters.ACTION_PLANS, actionPlans);
-    addMandatoryFacetValues(results, IssueFilterParameters.COMPONENT_UUIDS, request.paramAsStrings(IssueFilterParameters.COMPONENT_UUIDS));
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.ACTION_PLANS, actionPlans);
+    addMandatoryValuesToFacet(facets, IssueFilterParameters.COMPONENT_UUIDS, request.paramAsStrings(IssueFilterParameters.COMPONENT_UUIDS));
 
-    json.name("facets").beginArray();
     for (String facetName : options.getFacets()) {
-      json.beginObject();
-      json.prop("property", facetName);
-      json.name("values").beginArray();
-      LinkedHashMap<String, Long> buckets = results.getFacets().get(facetName);
-      if (buckets != null) {
-        Set<String> itemsFromFacets = Sets.newHashSet();
-        for (Map.Entry<String, Long> bucket : buckets.entrySet()) {
-          itemsFromFacets.add(bucket.getKey());
-          json.beginObject();
-          json.prop("val", bucket.getKey());
-          json.prop("count", bucket.getValue());
-          json.endObject();
-        }
-        // Prevent appearance of a glitch value due to dedicated parameter for this facet
-        if (!IssueFilterParameters.FACET_ASSIGNED_TO_ME.equals(facetName)) {
-          addZeroFacetsForSelectedItems(request, facetName, itemsFromFacets, json);
+      LinkedHashMap<String, Long> buckets = facets.get(facetName);
+      if (!IssueFilterParameters.FACET_ASSIGNED_TO_ME.equals(facetName)) {
+        if (buckets != null) {
+          List<String> requestParams = request.paramAsStrings(facetName);
+          if (requestParams != null) {
+            for (String param : requestParams) {
+              if (!buckets.containsKey(param) && !IssueQueryService.LOGIN_MYSELF.equals(param)) {
+                // Prevent appearance of a glitch value due to dedicated parameter for this facet
+                buckets.put(param, 0L);
+              }
+            }
+          }
         }
       }
-      json.endArray().endObject();
-    }
-    json.endArray();
-  }
-
-  private void collectFacetsData(Request request, SearchResult<IssueDoc> result, Set<String> projectUuids, Set<String> componentUuids, List<String> userLogins,
-    Set<String> actionPlanKeys) {
-    collectBucketKeys(result, IssueFilterParameters.PROJECT_UUIDS, projectUuids);
-    collectParameterValues(request, IssueFilterParameters.PROJECT_UUIDS, projectUuids);
-
-    collectBucketKeys(result, IssueFilterParameters.COMPONENT_UUIDS, componentUuids);
-    collectParameterValues(request, IssueFilterParameters.COMPONENT_UUIDS, componentUuids);
-    collectBucketKeys(result, IssueFilterParameters.FILE_UUIDS, componentUuids);
-    collectParameterValues(request, IssueFilterParameters.FILE_UUIDS, componentUuids);
-
-    collectBucketKeys(result, IssueFilterParameters.MODULE_UUIDS, componentUuids);
-    collectParameterValues(request, IssueFilterParameters.MODULE_UUIDS, componentUuids);
-    collectParameterValues(request, IssueFilterParameters.COMPONENT_ROOT_UUIDS, componentUuids);
-
-    collectBucketKeys(result, IssueFilterParameters.ASSIGNEES, userLogins);
-    collectParameterValues(request, IssueFilterParameters.ASSIGNEES, userLogins);
-    collectBucketKeys(result, IssueFilterParameters.REPORTERS, userLogins);
-    collectParameterValues(request, IssueFilterParameters.REPORTERS, userLogins);
-    collectBucketKeys(result, IssueFilterParameters.ACTION_PLANS, actionPlanKeys);
-    collectParameterValues(request, IssueFilterParameters.ACTION_PLANS, actionPlanKeys);
-  }
-
-  private static void collectBucketKeys(SearchResult<IssueDoc> result, String facetName, Collection<String> bucketKeys) {
-    bucketKeys.addAll(result.getFacets().getBucketKeys(facetName));
-  }
-
-  private static void collectParameterValues(Request request, String facetName, Collection<String> facetKeys) {
-    Collection<String> paramValues = request.paramAsStrings(facetName);
-    if (paramValues != null) {
-      facetKeys.addAll(paramValues);
-    }
-  }
-
-  // TODO change to use the RuleMapper
-  private void writeRules(JsonWriter json, Collection<Rule> rules) {
-    json.name("rules").beginArray();
-    for (Rule rule : rules) {
-      json.beginObject()
-        .prop("key", rule.key().toString())
-        .prop("name", rule.name())
-        .prop("lang", rule.language())
-        .prop("desc", rule.htmlDescription())
-        .prop("status", rule.status().toString());
-      Language lang = languages.get(rule.language());
-      json.prop("langName", lang == null ? null : lang.getName());
-      json.endObject();
-    }
-    json.endArray();
-  }
-
-  private void writeIssues(SearchResult<IssueDoc> result, Multimap<String, DefaultIssueComment> commentsByIssues, Map<String, User> usersByLogin,
-    Map<String, ActionPlan> actionPlanByKeys,
-    Map<String, ComponentDto> componentsByUuid, Map<String, ComponentDto> projectsByComponentUuid, @Nullable List<String> fields, JsonWriter json) {
-    json.name("issues").beginArray();
-
-    for (IssueDoc issue : result.getDocs()) {
-      issueWriter.write(json, issue, usersByLogin, componentsByUuid, projectsByComponentUuid, commentsByIssues, actionPlanByKeys, fields);
-    }
-
-    json.endArray();
-  }
-
-  private void writeComponents(JsonWriter json, Collection<ComponentDto> components, Map<String, ComponentDto> projectsByComponentUuid) {
-    json.name("components").beginArray();
-    for (ComponentDto component : components) {
-      ComponentDto project = projectsByComponentUuid.get(component.uuid());
-      componentWriter.write(json, component, project);
-    }
-    json.endArray();
-  }
-
-  private static void writeProjects(JsonWriter json, List<ComponentDto> projects) {
-    json.name("projects").beginArray();
-    for (ComponentDto project : projects) {
-      json.beginObject()
-        .prop("uuid", project.uuid())
-        .prop("key", project.key())
-        .prop("id", project.getId())
-        .prop("qualifier", project.qualifier())
-        .prop("name", project.name())
-        .prop("longName", project.longName())
-        .endObject();
     }
-    json.endArray();
   }
 
-  private void writeUsers(JsonWriter json, Map<String, User> usersByLogin) {
-    json.name("users").beginArray();
-    for (User user : usersByLogin.values()) {
-      userWriter.write(json, user);
-    }
-    json.endArray();
-  }
-
-  private void writeLanguages(JsonWriter json) {
-    json.name("languages").beginArray();
-    for (Language language : languages.all()) {
-      json.beginObject()
-        .prop("key", language.getKey())
-        .prop("name", language.getName())
-        .endObject();
-    }
-    json.endArray();
-  }
-
-  private void writeActionPlans(JsonWriter json, Collection<ActionPlan> plans) {
-    if (!plans.isEmpty()) {
-      json.name("actionPlans").beginArray();
-      for (ActionPlan actionPlan : plans) {
-        Date deadLine = actionPlan.deadLine();
-        Date updatedAt = actionPlan.updatedAt();
-
-        json.beginObject()
-          .prop("key", actionPlan.key())
-          .prop("name", actionPlan.name())
-          .prop("status", actionPlan.status())
-          .prop("project", actionPlan.projectKey())
-          .prop("userLogin", actionPlan.userLogin())
-          .prop("deadLine", isoDate(deadLine))
-          .prop("fDeadLine", formatDate(deadLine))
-          .prop("createdAt", isoDate(actionPlan.createdAt()))
-          .prop("fCreatedAt", formatDate(actionPlan.createdAt()))
-          .prop("updatedAt", isoDate(actionPlan.updatedAt()))
-          .prop("fUpdatedAt", formatDate(updatedAt))
-          .endObject();
+  private void addMandatoryValuesToFacet(Facets facets, String facetName, @Nullable Iterable<String> mandatoryValues) {
+    Map<String, Long> buckets = facets.get(facetName);
+    if (buckets != null && mandatoryValues != null) {
+      for (String mandatoryValue : mandatoryValues) {
+        if (!buckets.containsKey(mandatoryValue)) {
+          buckets.put(mandatoryValue, 0L);
+        }
       }
-      json.endArray();
-    }
-  }
-
-  private Map<String, User> getUsersByLogin(List<String> userLogins) {
-    Map<String, User> usersByLogin = newHashMap();
-    for (User user : userFinder.findByLogins(userLogins)) {
-      usersByLogin.put(user.login(), user);
-    }
-    return usersByLogin;
-  }
-
-  private Map<String, ActionPlan> getActionPlanByKeys(Collection<String> actionPlanKeys) {
-    Map<String, ActionPlan> actionPlans = newHashMap();
-    for (ActionPlan actionPlan : actionPlanService.findByKeys(actionPlanKeys)) {
-      actionPlans.put(actionPlan.key(), actionPlan);
     }
-    return actionPlans;
   }
 
-  @CheckForNull
-  private static String isoDate(@Nullable Date date) {
-    if (date != null) {
-      return DateUtils.formatDateTime(date);
+  private void collectLoggedInUser(SearchResponseLoader.Collector collector) {
+    if (userSession.isLoggedIn()) {
+      collector.add(SearchAdditionalField.USERS, userSession.getLogin());
     }
-    return null;
   }
 
-  @CheckForNull
-  private String formatDate(@Nullable Date date) {
-    if (date != null) {
-      return i18n.formatDateTime(userSession.locale(), date);
+  private void collectFacets(SearchResponseLoader.Collector collector, Facets facets) {
+    Set<String> facetRules = facets.getBucketKeys(IssueFilterParameters.RULES);
+    if (facetRules != null) {
+      collector.addAll(SearchAdditionalField.RULES, from(facetRules).transform(RuleKeyFunctions.stringToRuleKey()));
     }
-    return null;
+    collector.addProjectUuids(facets.getBucketKeys(IssueFilterParameters.PROJECT_UUIDS));
+    collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.COMPONENT_UUIDS));
+    collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.FILE_UUIDS));
+    collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.MODULE_UUIDS));
+    collector.addAll(SearchAdditionalField.USERS, facets.getBucketKeys(IssueFilterParameters.ASSIGNEES));
+    collector.addAll(SearchAdditionalField.USERS, facets.getBucketKeys(IssueFilterParameters.REPORTERS));
+    collector.addAll(SearchAdditionalField.ACTION_PLANS, facets.getBucketKeys(IssueFilterParameters.ACTION_PLANS));
   }
 
-  protected void addMandatoryFacetValues(SearchResult<IssueDoc> results, String facetName, @Nullable List<String> mandatoryValues) {
-    Map<String, Long> buckets = results.getFacets().get(facetName);
-    if (buckets != null && mandatoryValues != null) {
-      for (String mandatoryValue : mandatoryValues) {
-        if (!buckets.containsKey(mandatoryValue)) {
-          buckets.put(mandatoryValue, 0L);
-        }
-      }
-    }
+  private void collectRequestParams(SearchResponseLoader.Collector collector, Request request) {
+    collector.addProjectUuids(request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));
+    collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.FILE_UUIDS));
+    collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.MODULE_UUIDS));
+    collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOT_UUIDS));
+    collector.addAll(SearchAdditionalField.USERS, request.paramAsStrings(IssueFilterParameters.ASSIGNEES));
+    collector.addAll(SearchAdditionalField.USERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
+    collector.addAll(SearchAdditionalField.ACTION_PLANS, request.paramAsStrings(IssueFilterParameters.ACTION_PLANS));
   }
 
-  private static void addZeroFacetsForSelectedItems(Request request, String facetName, Set<String> itemsFromFacets, JsonWriter json) {
-    List<String> requestParams = request.paramAsStrings(facetName);
-    if (requestParams != null) {
-      for (String param : requestParams) {
-        if (!itemsFromFacets.contains(param) && !IssueQueryService.LOGIN_MYSELF.equals(param)) {
-          json.beginObject();
-          json.prop("val", param);
-          json.prop("count", 0);
-          json.endObject();
-        }
-      }
+  private enum IssueDocToKey implements Function<IssueDoc, String> {
+    INSTANCE;
+    @Override
+    public String apply(IssueDoc input) {
+      return input.key();
     }
   }
 }
index 25a553b908f0bfb861c933f351002049bc9cfa4d..dd91ba9f6ea99fdf11572e4aca8e937b4c5099ee 100644 (file)
@@ -67,7 +67,7 @@ public enum SearchAdditionalField {
   }
 
   public static EnumSet<SearchAdditionalField> getFromRequest(Request request) {
-    List<String> labels = request.paramAsStrings(Search2Action.ADDITIONAL_FIELDS);
+    List<String> labels = request.paramAsStrings(SearchAction.ADDITIONAL_FIELDS);
     if (labels == null) {
       return EnumSet.noneOf(SearchAdditionalField.class);
     }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java
new file mode 100644 (file)
index 0000000..4f65287
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.rule.Severity;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.issue.IssueService;
+
+public class SetSeverityAction implements IssuesWsAction {
+
+  public static final String ACTION = "set_severity";
+
+  private final IssueService issueService;
+  private final OperationResponseWriter responseWriter;
+
+  public SetSeverityAction(IssueService issueService, OperationResponseWriter responseWriter) {
+    this.issueService = issueService;
+    this.responseWriter = responseWriter;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction(ACTION)
+      .setDescription("Change severity. Requires authentication and Browse permission on project")
+      .setSince("3.6")
+      .setHandler(this)
+      .setPost(true);
+
+    action.createParam("issue")
+      .setDescription("Key of the issue")
+      .setRequired(true)
+      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
+    action.createParam("severity")
+      .setDescription("New severity")
+      .setRequired(true)
+      .setPossibleValues(Severity.ALL);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String key = request.mandatoryParam("issue");
+    issueService.setSeverity(key, request.mandatoryParam("severity"));
+
+    responseWriter.write(key, request, response);
+  }
+}
index 2ac4baecc072466e86f7b2432e0e3a645023d0f1..e86eb3208d544eb2a92a45209359d4de161a8a9f 100644 (file)
@@ -61,7 +61,6 @@ import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.component.ComponentService;
 import org.sonar.server.component.DefaultComponentFinder;
 import org.sonar.server.component.DefaultRubyComponentService;
-import org.sonar.server.component.ws.ComponentJsonWriter;
 import org.sonar.server.component.ws.ComponentsWs;
 import org.sonar.server.component.ws.EventsWs;
 import org.sonar.server.component.ws.ResourcesWs;
@@ -153,15 +152,7 @@ import org.sonar.server.issue.notification.MyNewIssuesNotificationDispatcher;
 import org.sonar.server.issue.notification.NewIssuesEmailTemplate;
 import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
 import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
-import org.sonar.server.issue.ws.ComponentTagsAction;
-import org.sonar.server.issue.ws.IssueActionsWriter;
-import org.sonar.server.issue.ws.IssueComponentHelper;
-import org.sonar.server.issue.ws.IssueJsonWriter;
-import org.sonar.server.issue.ws.IssuesWs;
-import org.sonar.server.issue.ws.OperationResponseWriter;
-import org.sonar.server.issue.ws.SearchResponseFormat;
-import org.sonar.server.issue.ws.SearchResponseLoader;
-import org.sonar.server.issue.ws.SetTagsAction;
+import org.sonar.server.issue.ws.IssueWsModule;
 import org.sonar.server.language.ws.LanguageWs;
 import org.sonar.server.measure.MeasureFilterEngine;
 import org.sonar.server.measure.MeasureFilterExecutor;
@@ -178,9 +169,9 @@ import org.sonar.server.notification.NotificationCenter;
 import org.sonar.server.notification.NotificationService;
 import org.sonar.server.notification.email.AlertsEmailTemplate;
 import org.sonar.server.notification.email.EmailNotificationChannel;
+import org.sonar.server.permission.PermissionFinder;
 import org.sonar.server.permission.PermissionService;
 import org.sonar.server.permission.PermissionTemplateService;
-import org.sonar.server.permission.PermissionFinder;
 import org.sonar.server.permission.ws.PermissionsWsModule;
 import org.sonar.server.platform.BackendCleanup;
 import org.sonar.server.platform.SettingsChangeNotifier;
@@ -353,16 +344,16 @@ public class PlatformLevel4 extends PlatformLevel {
       IndexDefinitions.class,
       IndexCreator.class,
 
-      // Activity
+    // Activity
       ActivityService.class,
       ActivityIndexDefinition.class,
       ActivityIndexer.class,
       ActivityIndex.class,
 
-      // batch
+    // batch
       BatchWsModule.class,
 
-      // Dashboard
+    // Dashboard
       DashboardsWs.class,
       org.sonar.server.dashboard.ws.ShowAction.class,
       ProjectDefaultDashboard.class,
@@ -400,12 +391,12 @@ public class PlatformLevel4 extends PlatformLevel {
       ProjectIssueFilterWidget.class,
       IssueTagCloudWidget.class,
 
-      // update center
+    // update center
       UpdateCenterClient.class,
       UpdateCenterMatrixFactory.class,
       UpdateCenterWs.class,
 
-      // quality profile
+    // quality profile
       XMLProfileParser.class,
       XMLProfileSerializer.class,
       AnnotationProfileParser.class,
@@ -448,7 +439,7 @@ public class PlatformLevel4 extends PlatformLevel {
       QProfileReset.class,
       RubyQProfileActivityService.class,
 
-      // rule
+    // rule
       AnnotationRuleParser.class,
       XMLRuleParser.class,
       DefaultRuleFinder.class,
@@ -476,17 +467,17 @@ public class PlatformLevel4 extends PlatformLevel {
       RepositoriesAction.class,
       org.sonar.server.rule.ws.AppAction.class,
 
-      // languages
+    // languages
       Languages.class,
       LanguageWs.class,
       org.sonar.server.language.ws.ListAction.class,
 
-      // activity
+    // activity
       ActivitiesWs.class,
       org.sonar.server.activity.ws.SearchAction.class,
       ActivityMapping.class,
 
-      // measure
+    // measure
       MeasureFilterFactory.class,
       MeasureFilterExecutor.class,
       MeasureFilterEngine.class,
@@ -498,14 +489,14 @@ public class PlatformLevel4 extends PlatformLevel {
       DefaultMetricFinder.class,
       TimeMachineWs.class,
 
-      // quality gates
+    // quality gates
       QualityGateDao.class,
       QualityGateConditionDao.class,
       QualityGates.class,
       ProjectQgateAssociationDao.class,
       QgateProjectFinder.class,
 
-      org.sonar.server.qualitygate.ws.ListAction.class,
+    org.sonar.server.qualitygate.ws.ListAction.class,
       org.sonar.server.qualitygate.ws.SearchAction.class,
       org.sonar.server.qualitygate.ws.ShowAction.class,
       org.sonar.server.qualitygate.ws.CreateAction.class,
@@ -522,17 +513,17 @@ public class PlatformLevel4 extends PlatformLevel {
       org.sonar.server.qualitygate.ws.AppAction.class,
       QGatesWs.class,
 
-      // web services
+    // web services
       WebServiceEngine.class,
       ListingWs.class,
 
-      // localization
+    // localization
       L10nWs.class,
 
-      // authentication
+    // authentication
       AuthenticationWs.class,
 
-      // users
+    // users
       SecurityRealmFactory.class,
       DeprecatedUserFinder.class,
       NewUserNotifier.class,
@@ -547,7 +538,6 @@ public class PlatformLevel4 extends PlatformLevel {
       CurrentAction.class,
       org.sonar.server.user.ws.SearchAction.class,
       org.sonar.server.user.ws.GroupsAction.class,
-      org.sonar.server.issue.ws.AuthorsAction.class,
       FavoritesWs.class,
       UserPropertiesWs.class,
       UserIndexDefinition.class,
@@ -555,19 +545,19 @@ public class PlatformLevel4 extends PlatformLevel {
       UserIndex.class,
       UserUpdater.class,
 
-      // groups
+    // groups
       GroupMembershipService.class,
       GroupMembershipFinder.class,
       UserGroupsModule.class,
 
-      // permissions
+    // permissions
       PermissionRepository.class,
       PermissionService.class,
       PermissionTemplateService.class,
       PermissionFinder.class,
       PermissionsWsModule.class,
 
-      // components
+    // components
       ProjectsWsModule.class,
       DefaultComponentFinder.class,
       DefaultRubyComponentService.class,
@@ -581,14 +571,13 @@ public class PlatformLevel4 extends PlatformLevel {
       NewAlerts.class,
       NewAlerts.newMetadata(),
       ComponentCleanerService.class,
-      ComponentJsonWriter.class,
 
-      // views
+    // views
       ViewIndexDefinition.class,
       ViewIndexer.class,
       ViewIndex.class,
 
-      // issues
+    // issues
       IssueIndexDefinition.class,
       IssueIndexer.class,
       IssueAuthorizationIndexer.class,
@@ -603,22 +592,9 @@ public class PlatformLevel4 extends PlatformLevel {
       Actions.class,
       IssueBulkChangeService.class,
       IssueChangelogFormatter.class,
-      IssuesWs.class,
-      IssueJsonWriter.class,
-      IssueComponentHelper.class,
       WsResponseCommonFormat.class,
-      SearchResponseLoader.class,
-      SearchResponseFormat.class,
-      OperationResponseWriter.class,
-      org.sonar.server.issue.ws.AssignAction.class,
-      org.sonar.server.issue.ws.ShowAction.class,
-      org.sonar.server.issue.ws.SearchAction.class,
-      org.sonar.server.issue.ws.Search2Action.class,
-      org.sonar.server.issue.ws.TagsAction.class,
-      SetTagsAction.class,
-      ComponentTagsAction.class,
+      IssueWsModule.class,
       IssueService.class,
-      IssueActionsWriter.class,
       IssueQueryService.class,
       NewIssuesEmailTemplate.class,
       MyNewIssuesEmailTemplate.class,
@@ -635,13 +611,13 @@ public class PlatformLevel4 extends PlatformLevel {
       EmailNotificationChannel.class,
       AlertsEmailTemplate.class,
 
-      IssueFilterWsModule.class,
+    IssueFilterWsModule.class,
 
-      // action plan
+    // action plan
       ActionPlanWs.class,
       ActionPlanService.class,
 
-      // issues actions
+    // issues actions
       AssignAction.class,
       PlanAction.class,
       SetSeverityAction.class,
@@ -650,7 +626,7 @@ public class PlatformLevel4 extends PlatformLevel {
       AddTagsAction.class,
       RemoveTagsAction.class,
 
-      // technical debt
+    // technical debt
       DebtModelService.class,
       DebtModelOperations.class,
       DebtModelLookup.class,
@@ -660,7 +636,7 @@ public class PlatformLevel4 extends PlatformLevel {
       DebtRulesXMLImporter.class,
       DebtCharacteristicsXMLImporter.class,
 
-      // source
+    // source
       HtmlSourceDecorator.class,
       SourceService.class,
       SourcesWs.class,
@@ -674,23 +650,23 @@ public class PlatformLevel4 extends PlatformLevel {
       SourceLineIndex.class,
       SourceLineIndexer.class,
 
-      // Duplications
+    // Duplications
       DuplicationsParser.class,
       DuplicationsWs.class,
       DuplicationsJsonWriter.class,
       org.sonar.server.duplication.ws.ShowAction.class,
 
-      // text
+    // text
       MacroInterpreter.class,
       RubyTextService.class,
 
-      // Notifications
+    // Notifications
       EmailSettings.class,
       NotificationService.class,
       NotificationCenter.class,
       DefaultNotificationManager.class,
 
-      // Tests
+    // Tests
       CoverageService.class,
       TestsWs.class,
       CoveredFilesAction.class,
@@ -699,12 +675,12 @@ public class PlatformLevel4 extends PlatformLevel {
       TestIndex.class,
       TestIndexer.class,
 
-      // Properties
+    // Properties
       PropertiesWs.class,
 
-      TypeValidationModule.class,
+    TypeValidationModule.class,
 
-      // System
+    // System
       RestartAction.class,
       InfoAction.class,
       UpgradesAction.class,
@@ -719,7 +695,7 @@ public class PlatformLevel4 extends PlatformLevel {
       MigrateDbAction.class,
       DbMigrationStatusAction.class,
 
-      // Plugins WS
+    // Plugins WS
       PluginWSCommons.class,
       PluginUpdateAggregator.class,
       InstalledAction.class,
@@ -732,7 +708,7 @@ public class PlatformLevel4 extends PlatformLevel {
       CancelAllAction.class,
       PluginsWs.class,
 
-      // Compute engine
+    // Compute engine
       CEQueueStatusImpl.class,
       ComputeEngineQueueMonitor.class,
       ReportQueue.class,
@@ -746,7 +722,7 @@ public class PlatformLevel4 extends PlatformLevel {
       ProjectSettingsFactory.class,
       IndexPurgeListener.class,
 
-      // UI
+    // UI
       GlobalNavigationAction.class,
       SettingsNavigationAction.class,
       ComponentNavigationAction.class,
index 51022792640c39db6258b6f9a5bc053def10ee22..b184a8282797966c17a26b4e19f0a86be795a2e4 100644 (file)
@@ -32,30 +32,22 @@ import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.issue.ActionPlan;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.action.Action;
 import org.sonar.api.user.User;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.FieldDiffs;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
 import org.sonar.db.component.ResourceDao;
 import org.sonar.db.component.ResourceDto;
 import org.sonar.db.component.ResourceQuery;
 import org.sonar.db.issue.IssueFilterDto;
-import org.sonar.server.component.ws.ComponentJsonWriter;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.Message;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.filter.IssueFilterService;
-import org.sonar.server.issue.ws.IssueComponentHelper;
-import org.sonar.server.issue.ws.IssueJsonWriter;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.user.ThreadLocalUserSession;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.user.ws.UserJsonWriter;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.newHashMap;
@@ -86,28 +78,12 @@ public class InternalRubyIssueServiceTest {
 
   ResourceDao resourceDao;
 
-  ActionService actionService;
-
   IssueFilterService issueFilterService;
 
   IssueBulkChangeService issueBulkChangeService;
 
   InternalRubyIssueService service;
 
-  IssueJsonWriter issueWriter;
-
-  IssueComponentHelper issueComponentHelper;
-
-  UserIndex userIndex;
-
-  DbClient dbClient;
-
-  DbSession dbSession;
-
-  UserJsonWriter userWriter;
-
-  ComponentJsonWriter componentWriter;
-
   @Before
   public void setUp() {
     issueService = mock(IssueService.class);
@@ -116,22 +92,14 @@ public class InternalRubyIssueServiceTest {
     changelogService = mock(IssueChangelogService.class);
     actionPlanService = mock(ActionPlanService.class);
     resourceDao = mock(ResourceDao.class);
-    actionService = mock(ActionService.class);
     issueFilterService = mock(IssueFilterService.class);
     issueBulkChangeService = mock(IssueBulkChangeService.class);
-    issueWriter = mock(IssueJsonWriter.class);
-    issueComponentHelper = mock(IssueComponentHelper.class);
-    userIndex = mock(UserIndex.class);
-    dbClient = mock(DbClient.class);
-    dbSession = mock(DbSession.class);
-    userWriter = mock(UserJsonWriter.class);
-    userWriter = mock(UserJsonWriter.class);
 
     ResourceDto project = new ResourceDto().setKey("org.sonar.Sample");
     when(resourceDao.selectResource(any(ResourceQuery.class))).thenReturn(project);
 
-    service = new InternalRubyIssueService(issueService, issueQueryService, commentService, changelogService, actionPlanService, resourceDao, actionService,
-      issueFilterService, issueBulkChangeService, issueWriter, issueComponentHelper, componentWriter, userIndex, dbClient, userSessionRule, userWriter);
+    service = new InternalRubyIssueService(issueService, issueQueryService, commentService, changelogService, actionPlanService, resourceDao,
+      issueFilterService, issueBulkChangeService, userSessionRule);
   }
 
   @Test
@@ -170,12 +138,6 @@ public class InternalRubyIssueServiceTest {
     verify(commentService).findComments(newArrayList("ABCD"));
   }
 
-  @Test
-  public void do_transition() {
-    service.doTransition("ABCD", Issue.STATUS_RESOLVED);
-    verify(issueService).doTransition(eq("ABCD"), eq(Issue.STATUS_RESOLVED));
-  }
-
   @Test
   public void create_action_plan() {
     Map<String, String> parameters = newHashMap();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/CreateActionTest.java
new file mode 100644 (file)
index 0000000..c0ed4dc
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.ws.WsAction;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CreateActionTest {
+
+  IssueService issueService = mock(IssueService.class);
+  OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
+  WsAction underTest = new CreateAction(issueService, responseWriter);
+  WsActionTester tester = new WsActionTester(underTest);
+
+  @Test
+  public void create_manual_issue_with_default_values() throws Exception {
+    RuleKey ruleKey = RuleKey.of(RuleKey.MANUAL_REPOSITORY_KEY, "S1");
+    when(issueService.createManualIssue("FILE_KEY", ruleKey, null, null, null, null))
+      .thenReturn(new DefaultIssue().setKey("ISSUE_KEY"));
+
+    tester.newRequest()
+      .setParam("component", "FILE_KEY")
+      .setParam("rule", ruleKey.toString())
+      .execute();
+
+    verify(issueService).createManualIssue("FILE_KEY", ruleKey, null, null, null, null);
+    verify(responseWriter).write(eq("ISSUE_KEY"), any(Request.class), any(Response.class));
+  }
+
+  @Test
+  public void create_manual_issue() throws Exception {
+    RuleKey ruleKey = RuleKey.of(RuleKey.MANUAL_REPOSITORY_KEY, "S1");
+    when(issueService.createManualIssue("FILE_KEY", ruleKey, 13, "the msg", "BLOCKER", null))
+      .thenReturn(new DefaultIssue().setKey("ISSUE_KEY"));
+
+    tester.newRequest()
+      .setParam("component", "FILE_KEY")
+      .setParam("rule", ruleKey.toString())
+      .setParam("severity", "BLOCKER")
+      .setParam("line", "13")
+      .setParam("message", "the msg")
+      .execute();
+
+    verify(issueService).createManualIssue("FILE_KEY", ruleKey, 13, "the msg", "BLOCKER", null);
+    verify(responseWriter).write(eq("ISSUE_KEY"), any(Request.class), any(Response.class));
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/DoTransitionActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/DoTransitionActionTest.java
new file mode 100644 (file)
index 0000000..b504415
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.junit.Test;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.ws.WsAction;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DoTransitionActionTest {
+
+  IssueService issueService = mock(IssueService.class);
+  OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
+  WsAction underTest = new DoTransitionAction(issueService, responseWriter);
+  WsActionTester tester = new WsActionTester(underTest);
+
+  @Test
+  public void do_transition() throws Exception {
+    tester.newRequest()
+      .setParam("issue", "ABC")
+      .setParam("transition", "confirm")
+      .execute();
+
+    verify(issueService).doTransition("ABC", "confirm");
+    verify(responseWriter).write(eq("ABC"), any(Request.class), any(Response.class));
+  }
+
+}
index 7e9eb3d2d57a54eaf23aa28a2ca18e8c6a4806a3..050ce927fe1980ef846ae9becd98a5e188111430 100644 (file)
@@ -33,7 +33,6 @@ import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.workflow.Transition;
-import org.sonar.server.issue.ActionService;
 import org.sonar.server.issue.IssueService;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.test.JsonAssert;
@@ -50,14 +49,11 @@ public class IssueActionsWriterTest {
   @Mock
   IssueService issueService;
 
-  @Mock
-  ActionService actionService;
-
   IssueActionsWriter writer;
 
   @Before
   public void setUp() {
-    writer = new IssueActionsWriter(issueService, actionService, userSessionRule);
+    writer = new IssueActionsWriter(issueService, userSessionRule);
   }
 
   @Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueWsModuleTest.java
new file mode 100644 (file)
index 0000000..c3a20b1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueWsModuleTest {
+  @Test
+  public void verify_count_of_added_components() {
+    ComponentContainer container = new ComponentContainer();
+    new IssueWsModule().configure(container);
+    assertThat(container.size()).isEqualTo(18);
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/PlanActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/PlanActionTest.java
new file mode 100644 (file)
index 0000000..9ee14c9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.junit.Test;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.ws.WsAction;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class PlanActionTest {
+
+  IssueService issueService = mock(IssueService.class);
+  OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
+  WsAction underTest = new PlanAction(issueService, responseWriter);
+  WsActionTester tester = new WsActionTester(underTest);
+
+  @Test
+  public void plan() throws Exception {
+    tester.newRequest()
+      .setParam("issue", "ABC")
+      .setParam("plan", "P1")
+      .execute();
+
+    verify(issueService).plan("ABC", "P1");
+    verify(responseWriter).write(eq("ABC"), any(Request.class), any(Response.class));
+  }
+
+  @Test
+  public void unplan_if_value_is_absent() throws Exception {
+    tester.newRequest()
+      .setParam("issue", "ABC")
+      .execute();
+
+    verify(issueService).plan("ABC", null);
+    verify(responseWriter).write(eq("ABC"), any(Request.class), any(Response.class));
+  }
+}
index e1238e03686072ff80b50e7c03d7468dbd441726..90274dd1f73a0bd584c9bb323a1864bbbebc30cc 100644 (file)
@@ -107,7 +107,7 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
     result.assertJson(this.getClass(), "issues_on_different_projects.json");
   }
 
@@ -121,22 +121,22 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.PROJECT_UUIDS, project.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_project_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.PROJECT_UUIDS, "unknown")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_project_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, "unknown")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -161,7 +161,7 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.PROJECT_UUIDS, project1.uuid())
       .setParam(WebService.Param.FACETS, "projectUuids")
       .execute()
@@ -178,22 +178,22 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.FILE_UUIDS, file.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_file_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.FILE_UUIDS, "unknown")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, file.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_file_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, "unknown")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -212,12 +212,12 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENTS, file.key())
       .execute()
       .assertJson(this.getClass(), "search_by_file_key.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENTS, unitTest.key())
       .execute()
       .assertJson(this.getClass(), "search_by_test_key.json");
@@ -238,7 +238,7 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid())
       .setParam(IssueFilterParameters.FILE_UUIDS, file1.uuid() + "," + file3.uuid())
       .setParam(WebService.Param.FACETS, "fileUuids")
@@ -257,22 +257,22 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, directory.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_file_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, "unknown")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
       .execute()
       .assertJson(this.getClass(), "search_by_file_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.DIRECTORIES, "src/main/java")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -295,34 +295,34 @@ public class SearchActionComponentsMediumTest {
 
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, directory1.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_directory_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, directory2.uuid())
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.MODULE_UUIDS, module1.uuid())
       .setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
       .execute()
       .assertJson(this.getClass(), "search_by_directory_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.MODULE_UUIDS, module2.uuid())
       .setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
       .execute()
       .assertJson(this.getClass(), "search_by_directory_uuid.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.DIRECTORIES, "src/main/java")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -345,7 +345,7 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, module.uuid())
       .setParam(IssueFilterParameters.MODULE_UUIDS, subModule1.uuid() + "," + subModule3.uuid())
       .setParam(WebService.Param.FACETS, "moduleUuids")
@@ -365,7 +365,7 @@ public class SearchActionComponentsMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam(WebService.Param.FACETS, "directories")
       .execute();
@@ -385,7 +385,7 @@ public class SearchActionComponentsMediumTest {
     setAnyoneProjectPermission(view, UserRole.USER);
     userSessionRule.login("john").addProjectUuidPermissions(UserRole.USER, view.uuid());
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, view.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_view_uuid.json");
@@ -405,7 +405,7 @@ public class SearchActionComponentsMediumTest {
     // User has wrong permission on the view, no issue will be returned
     userSessionRule.login("john").addProjectUuidPermissions(UserRole.CODEVIEWER, view.uuid());
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, view.uuid())
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -426,7 +426,7 @@ public class SearchActionComponentsMediumTest {
     setAnyoneProjectPermission(view, UserRole.USER);
     userSessionRule.login("john").addComponentUuidPermission(UserRole.USER, view.uuid(), subView.uuid());
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, subView.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_view_uuid.json");
@@ -448,7 +448,7 @@ public class SearchActionComponentsMediumTest {
     // User has wrong permission on the view, no issue will be returned
     userSessionRule.login("john").addComponentUuidPermission(UserRole.CODEVIEWER, view.uuid(), subView.uuid());
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, subView.uuid())
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -467,13 +467,13 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.AUTHORS, "leia")
       .setParam(WebService.Param.FACETS, "authors")
       .execute()
       .assertJson(this.getClass(), "search_by_authors.json");
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.AUTHORS, "unknown")
       .execute()
       .assertJson(this.getClass(), "no_issue.json");
@@ -496,7 +496,7 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, developer.uuid())
       .execute()
       .assertJson(this.getClass(), "search_by_developer.json");
@@ -528,7 +528,7 @@ public class SearchActionComponentsMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam(IssueFilterParameters.COMPONENT_UUIDS, technicalProject.uuid())
       .execute();
     result
index 168c21a6c0ebd6865090d096f093a23a163d95be..683b94a983cdc8a531a7b0f2044ac1cd25a9c1f9 100644 (file)
@@ -92,7 +92,7 @@ public class SearchActionMediumTest {
   public void define_action() {
     WebService.Controller controller = wsTester.controller("api/issues");
 
-    WebService.Action show = controller.action("search2");
+    WebService.Action show = controller.action("search");
     assertThat(show).isNotNull();
     assertThat(show.handler()).isNotNull();
     assertThat(show.since()).isEqualTo("3.6");
@@ -104,7 +104,7 @@ public class SearchActionMediumTest {
 
   @Test
   public void empty_search() throws Exception {
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
+    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
     WsTester.Result result = request.execute();
 
     assertThat(result).isNotNull();
@@ -137,7 +137,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
     result.assertJson(this.getClass(), "response_contains_all_fields_except_additional_fields.json");
   }
 
@@ -171,7 +171,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("additionalFields", "comments,users")
       .execute();
     result.assertJson(this.getClass(), "issue_with_comments.json");
@@ -207,7 +207,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).setParam(IssueFilterParameters.HIDE_COMMENTS, "true").execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(IssueFilterParameters.HIDE_COMMENTS, "true").execute();
     result.assertJson(this.getClass(), "issue_with_comment_hidden.json");
     assertThat(result.outputAsString()).doesNotContain("fabrice");
   }
@@ -235,7 +235,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("additionalFields", "actionPlans")
       .execute();
     result.assertJson(this.getClass(), "issue_with_action_plan.json");
@@ -254,7 +254,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .execute();
     result.assertJson(this.getClass(), "issue_with_attributes.json");
   }
@@ -285,7 +285,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("additionalFields", "_all").execute();
     result.assertJson(this.getClass(), "load_additional_fields.json");
   }
@@ -310,7 +310,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .execute();
     result.assertJson(this.getClass(), "issue_on_removed_file.json");
   }
@@ -325,7 +325,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
     assertThat(result.outputAsString()).contains("\"componentId\":" + file.getId() + ",");
   }
 
@@ -342,7 +342,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).setParam(IssueFilterParameters.COMPONENTS, file.getKey()).execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(IssueFilterParameters.COMPONENTS, file.getKey()).execute();
     result.assertJson(this.getClass(), "apply_paging_with_one_component.json");
   }
 
@@ -357,7 +357,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).setParam(Search2Action.ADDITIONAL_FIELDS, "_all").execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(SearchAction.ADDITIONAL_FIELDS, "_all").execute();
     result.assertJson(this.getClass(), "components_contains_sub_projects.json");
   }
 
@@ -378,7 +378,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam(WebService.Param.FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans")
       .execute();
@@ -402,7 +402,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam(WebService.Param.FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans")
       .setParam("facetMode", "debt")
@@ -427,7 +427,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam("severities", "MAJOR,MINOR")
       .setParam("languages", "xoo,polop,palap")
@@ -472,7 +472,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john");
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam("assignees", "__me__")
       .setParam(WebService.Param.FACETS, "assignees,assigned_to_me")
@@ -503,7 +503,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam("assignees", "__me__")
       .execute()
@@ -546,7 +546,7 @@ public class SearchActionMediumTest {
     tester.get(IssueIndexer.class).indexAll();
 
     userSessionRule.login("john-bob.polop");
-    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("resolved", "false")
       .setParam("assignees", "alice")
       .setParam(WebService.Param.FACETS, "assignees,assigned_to_me")
@@ -572,7 +572,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
       .setParam("sort", IssueQuery.SORT_BY_UPDATE_DATE)
       .setParam("asc", "false")
       .execute();
@@ -592,7 +592,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
+    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
     request.setParam(WebService.Param.PAGE, "2");
     request.setParam(WebService.Param.PAGE_SIZE, "9");
 
@@ -613,7 +613,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
+    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
     request.setParam(WebService.Param.PAGE, "1");
     request.setParam(WebService.Param.PAGE_SIZE, "-1");
 
@@ -634,7 +634,7 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
+    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
     request.setParam(IssueFilterParameters.PAGE_INDEX, "2");
     request.setParam(IssueFilterParameters.PAGE_SIZE, "9");
 
@@ -644,7 +644,7 @@ public class SearchActionMediumTest {
 
   @Test
   public void default_page_size_is_100() throws Exception {
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
+    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
 
     WsTester.Result result = request.execute();
     result.assertJson(this.getClass(), "default_page_size_is_100.json");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetSeverityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetSeverityActionTest.java
new file mode 100644 (file)
index 0000000..1869fee
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.ws.WsAction;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class SetSeverityActionTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  IssueService issueService = mock(IssueService.class);
+  OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
+  WsAction underTest = new SetSeverityAction(issueService, responseWriter);
+  WsActionTester tester = new WsActionTester(underTest);
+
+  @Test
+  public void set_severity() throws Exception {
+    tester.newRequest()
+      .setParam("issue", "ABC")
+      .setParam("severity", "BLOCKER")
+      .execute();
+
+    verify(issueService).setSeverity("ABC", "BLOCKER");
+    verify(responseWriter).write(eq("ABC"), any(Request.class), any(Response.class));
+  }
+
+  @Test
+  public void fail_if_bad_severity_value() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    tester.newRequest()
+      .setParam("issue", "ABC")
+      .setParam("severity", "WAT")
+      .execute();
+  }
+}
index 2e227df5e83cc627170cf01427e93aa35ef139f0..d88520ca2862aded2389fc8475257eec344eb2e6 100644 (file)
@@ -51,7 +51,6 @@ import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.server.component.ComponentTesting;
 import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.issue.ActionService;
 import org.sonar.server.issue.IssueChangelog;
 import org.sonar.server.issue.IssueChangelogService;
 import org.sonar.server.issue.IssueCommentService;
@@ -101,9 +100,6 @@ public class ShowActionTest {
   @Mock
   ActionPlanService actionPlanService;
 
-  @Mock
-  ActionService actionService;
-
   @Mock
   UserFinder userFinder;
 
@@ -150,9 +146,8 @@ public class ShowActionTest {
     tester = new WsTester(new IssuesWs(
       new ShowAction(
         dbClient, issueService, issueChangelogService, commentService,
-        new IssueActionsWriter(issueService, actionService, userSessionRule),
-        actionPlanService, userFinder, debtModel, ruleService, i18n, durations, userSessionRule)
-    ));
+        new IssueActionsWriter(issueService, userSessionRule),
+        actionPlanService, userFinder, debtModel, ruleService, i18n, durations, userSessionRule)));
   }
 
   @Test
@@ -431,8 +426,7 @@ public class ShowActionTest {
         .setKey("COMMENT-ABCE")
         .setMarkdownText("Another comment")
         .setUserLogin("arthur")
-        .setCreatedAt(date2)
-    ));
+        .setCreatedAt(date2)));
 
     when(userFinder.findByLogin("john")).thenReturn(new DefaultUser().setLogin("john").setName("John"));
     when(userFinder.findByLogin("arthur")).thenReturn(new DefaultUser().setLogin("arthur").setName("Arthur"));
index 9d06746a051c7f98ffae36e54b557db81a2e4b5a..deb21bae193a6a655dbd53b0f7eed96f776ddb19 100644 (file)
@@ -60,20 +60,6 @@ class Api::IssuesController < Api::ApiController
     )
   end
 
-  #
-  # POST /api/issues/do_transition?issue=<key>&transition=<key>&comment=<optional comment>
-  #
-  # -- Example
-  # curl -X POST -v -u admin:admin 'http://localhost:9000/api/issues/do_transition?issue=9b6f89c0-3347-46f6-a6d1-dd6c761240e0&transition=resolve'
-  #
-  def do_transition
-    verify_post_request
-    require_parameters :issue, :transition
-
-    result = Internal.issues.doTransition(params[:issue], params[:transition])
-    render_result_issue(result)
-  end
-
   #
   # POST /api/issues/add_comment?issue=<key>&text=<text>
   #
@@ -145,66 +131,6 @@ class Api::IssuesController < Api::ApiController
     end
   end
 
-
-  #
-  # Change the severity of an existing issue
-  #
-  # POST /api/issues/set_severity?issue=<key>&severity=<severity>
-  #
-  # -- Example
-  # curl -X POST -v -u admin:admin 'http://localhost:9000/api/issues/set_severity?issue=4a2881e7-825e-4140-a154-01f420c43d11&severity=BLOCKER'
-  #
-  def set_severity
-    verify_post_request
-    require_parameters :issue, :severity
-
-    result = Internal.issues.setSeverity(params[:issue], params[:severity])
-    render_result_issue(result)
-  end
-
-  #
-  # Link an existing issue to an action plan or unlink
-  #
-  # POST /api/issues/plan?issue=<key>&plan=<optional plan>
-  # A nil or blank plan removes the action plan.
-  #
-  # -- Example
-  # curl -X POST -v -u admin:admin 'http://localhost:9000/api/issues/plan?issue=4a2881e7-825e-4140-a154-01f420c43d11&plan=6b851f3c-e25c-432c-aee0-0e13a4184ca3'
-  #
-  def plan
-    verify_post_request
-    require_parameters :issue
-
-    plan = nil
-    plan = params[:plan] if params[:plan] && !params[:plan].blank?
-    result = Internal.issues.plan(params[:issue], plan)
-    render_result_issue(result)
-  end
-
-  #
-  # Create a manual issue.
-  #
-  # POST /api/issues/create
-  #
-  # -- Mandatory parameters
-  # 'component' is the component key
-  # 'rule' is the rule key prefixed with "manual:", for example 'manual:performance'
-  #
-  # -- Optional parameters
-  # 'severity' is in BLOCKER, CRITICAL, ... INFO. Default value is MAJOR.
-  # 'line' starts at 1
-  # 'message' is the plain-text message
-  #
-  # -- Example
-  # curl -X POST -v -u admin:admin 'http://localhost:9000/api/issues/create?component=commons-io:commons-io:org.apache.commons.io.filefilter.OrFileFilter&rule=manual:performance&line=2&severity=BLOCKER'
-  #
-  def create
-    verify_post_request
-
-    issue_result = Internal.issues.create(params)
-    render_result_issue(issue_result)
-  end
-
   #
   # GET /api/issues/actions?issue=<key>
   #
@@ -222,21 +148,6 @@ class Api::IssuesController < Api::ApiController
     )
   end
 
-
-  #
-  # POST /api/issues/do_action?issue=<key>&actionKey=<action key>
-  #
-  # -- Example
-  # curl -X POST -v -u admin:admin 'http://localhost:9000/api/issues/do_action?issue=9b6f89c0-3347-46f6-a6d1-dd6c761240e0&actionKey=link-to-jira'
-  #
-  def do_action
-    verify_post_request
-    require_parameters :issue, :actionKey
-
-    result = Internal.issues.executeAction(params[:issue], params[:actionKey])
-    render_result_issue(result)
-  end
-
   #
   # Execute a bulk change on a list of issues
   #
@@ -283,16 +194,6 @@ class Api::IssuesController < Api::ApiController
 
   protected
 
-  def render_result_issue(result)
-    hash = result_to_hash(result)
-    hash[:issue] = Issue.to_hash(result.get) if result.get
-
-    respond_to do |format|
-      # if the request header "Accept" is "*/*", then the default format is the first one (json)
-      format.json { render :json => Internal.issues.writeIssueJson(result.get), :status => result.httpStatus }
-      format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'sonar', :status => (result.ok ? 200 : 400)) }
-    end
-  end
 
   def result_to_hash(result)
     hash = {}