]> source.dussan.org Git - sonarqube.git/commitdiff
IssueShowWS : Add assign to me action
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 23 Jan 2014 13:14:04 +0000 (14:14 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 23 Jan 2014 13:14:04 +0000 (14:14 +0100)
18 files changed:
sonar-server/src/main/java/org/sonar/server/issue/ActionService.java
sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java
sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java
sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_severity_action.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json [new file with mode: 0644]
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueClient.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueClient.java
sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/DefaultIssueClientTest.java

index a79aead0e47fdff11f9f8a23b64b9154b3b9a778..ee9682ea820ffe4ae2252209188852dfa0720185 100644 (file)
@@ -71,6 +71,10 @@ public class ActionService implements ServerComponent {
     this.actions = actions;
   }
 
+  public List<Action> listAllActions() {
+    return actions.list();
+  }
+
   public List<Action> listAvailableActions(String issueKey) {
     IssueQueryResult queryResult = loadIssue(issueKey);
     final DefaultIssue issue = (DefaultIssue) queryResult.first();
index f4b3b2800646e8136f7f783c282df7341faa1d9e..63da1651fbff3101b993243dcb6ca50c852db4c5 100644 (file)
 package org.sonar.server.issue;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.ServerComponent;
@@ -60,6 +62,8 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
+import static com.google.common.collect.Lists.newArrayList;
+
 /**
  * All the issue features that are not published to public API.
  *
@@ -133,6 +137,15 @@ public class InternalRubyIssueService implements ServerComponent {
     return changelogService.formatDiffs(diffs);
   }
 
+  public List<String> listPluginActions() {
+    return newArrayList(Iterables.transform(actionService.listAllActions(), new Function<Action, String>() {
+      @Override
+      public String apply(@Nullable Action input) {
+        return input.key();
+      }
+    }));
+  }
+
   public Result<Issue> doTransition(String issueKey, String transitionKey) {
     Result<Issue> result = Result.of();
     try {
index 02fb37c8ee645c5dde82ddccfe8e1c35e4791f3f..c7c5df9e7e328c0279f8f39d82c716b70ac2b639 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.workflow.Transition;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.exceptions.NotFoundException;
@@ -124,7 +125,7 @@ public class IssueShowWsHandler implements RequestHandler {
     json.endArray();
   }
 
-  private void writeActions(Issue issue, JsonWriter json) {
+  private void writeActions(DefaultIssue issue, JsonWriter json) {
     json.name("actions").beginArray();
     for (String action : actions(issue)) {
       json.value(action);
@@ -133,13 +134,19 @@ public class IssueShowWsHandler implements RequestHandler {
   }
 
   // TODO all available actions should be returned by ActionService or another service
-  private List<String> actions(Issue issue) {
+  private List<String> actions(DefaultIssue issue) {
     List<String> actions = newArrayList();
     if (UserSession.get().isLoggedIn()) {
       actions.add("comment");
       if (issue.resolution() == null) {
         actions.add("assign");
+        if (!UserSession.get().login().equals(issue.assignee())) {
+          actions.add("assign_to_me");
+        }
         actions.add("plan");
+        if (UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey())) {
+          actions.add("severity");
+        }
         for (Action action : actionService.listAvailableActions(issue)) {
           actions.add(action.key());
         }
index 3f5f412f6d4dc9ad1d0ba943db3fcf53857db8bf..fc391b6c1683384b98f23ce6465f35ae07900298 100644 (file)
@@ -166,7 +166,7 @@ class Api::IssuesController < Api::ApiController
   #
   # Assign an existing issue to a user or un-assign.
   #
-  # POST /api/issues/assign?issue=<key>&assignee=<optional assignee>
+  # POST /api/issues/assign?issue=<key>&assignee=<optional assignee>&me=<true or false>
   # A nil or blank assignee removes the assignee.
   #
   # -- Example
@@ -176,7 +176,8 @@ class Api::IssuesController < Api::ApiController
     verify_post_request
     require_parameters :issue
 
-    result = Internal.issues.assign(params[:issue], params[:assignee])
+    assignee = (params[:me]=='true' ? current_user.login : params[:assignee])
+    result = Internal.issues.assign(params[:issue], assignee)
     render_result_issue(result)
   end
 
index fc552a538ebedd23e38935b7ef5c36887d22217d..44204a77e32dcabd3e60b508a11c73cf5b803f3a 100644 (file)
@@ -37,6 +37,8 @@
       'any':           '<%= escape_javascript message('any') -%>',
       'anytime':       '<%= escape_javascript message('anytime') -%>',
       'all':           '<%= escape_javascript message('all') -%>',
+      'edit':          '<%= escape_javascript message('edit') -%>',
+      'delete':        '<%= escape_javascript message('delete') -%>',
       'to':            '<%= escape_javascript message('to') -%>',
       'project':       '<%= escape_javascript message('issue_filter.criteria.project') -%>',
       'severity':      '<%= escape_javascript message('issue_filter.criteria.severity') -%>',
         resolved:      '<%= escape_javascript message('issue.status.RESOLVED') -%>',
         closed:        '<%= escape_javascript message('issue.status.CLOSED') -%>'
       },
+      'actions': {
+        comment:        '<%= escape_javascript message('issue.comment.formlink') -%>',
+        assign:         '<%= escape_javascript message('issue.assign.formlink') -%>',
+        assign_to_me:   '<%= escape_javascript message('issue.assign.to_me') -%>',
+        assigned_to:    '<%= escape_javascript message('assigned_to') -%>',
+        plan:           '<%= escape_javascript message('issue.do_plan') -%>',
+        planned_for:    '<%= escape_javascript message('issue.planned_for') -%>',
+        severity:       '<%= escape_javascript message('issue.set_severity') -%>',
+        <%  Internal.issues.listPluginActions().each do |action| %>
+          <%= action.key -%>: '<%= escape_javascript message("issue.action.#{action.key}.formlink") -%>',
+        <% end %>
+      },
       assignee:        '<%= escape_javascript message('issue_filter.criteria.assignee') -%>',
       resolution:      '<%= escape_javascript message('issue_filter.criteria.resolution') -%>',
       resolutions: {
@@ -67,7 +81,8 @@
 
       moreCriteria:    '<%= escape_javascript message('issue_filter.more_criteria') -%>',
       unassigned:      '<%= escape_javascript message('unassigned') -%>',
-      filtersList:     '<%= escape_javascript message('issue_filter.filter_list') -%>'
+      filtersList:     '<%= escape_javascript message('issue_filter.filter_list') -%>',
+      commentConfirmDelete:     '<%= escape_javascript message('issue.comment.delete_confirm_message') -%>',
     }
   });
 
index be2161dfbdce9e667f89a4bd7f500f19ecb13f63..325a096174dd69a682a356d88cad50e8b9b706b0 100644 (file)
@@ -240,6 +240,12 @@ public class ActionServiceTest {
     assertThat(result.hasKey("sonar.jira.project.key")).isTrue();
   }
 
+  @Test
+  public void list_all_actions() {
+    actions.add("link-to-jira").setConditions(new AlwaysMatch());
+    assertThat(actionService.listAllActions()).hasSize(1);
+  }
+
   public class AlwaysMatch implements Condition {
     @Override
     public boolean matches(Issue issue) {
index 08ef61282d082931597da0516ebf265dd612332a..b9dec4a89a84fb7aaee1b10a63d89bfc5c5f932d 100644 (file)
@@ -73,6 +73,12 @@ public class InternalRubyIssueServiceTest {
   }
 
   @Test
+  public void list_plugin_actions() {
+    service.listPluginActions();
+    verify(actionService).listAllActions();
+  }
+
+    @Test
   public void should_create_action_plan() {
     Map<String, String> parameters = newHashMap();
     parameters.put("name", "Long term");
index 5ff02ef0273a1df6f4665b32b19d096d11aff5fc..c0041331d3e514b96ef5083f756c1a860c25971d 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.api.rules.Rule;
 import org.sonar.api.server.ws.SimpleRequest;
 import org.sonar.api.user.User;
 import org.sonar.api.utils.DateUtils;
+import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.DefaultIssueQueryResult;
 import org.sonar.core.issue.workflow.Transition;
@@ -210,7 +211,7 @@ public class IssueShowWsHandlerTest {
 
   @Test
   public void show_issue_with_transitions() throws Exception {
-    Issue issue = createStandardIssue()
+    DefaultIssue issue = createStandardIssue()
       .setStatus("RESOLVED")
       .setResolution("FIXED");
     issues.add(issue);
@@ -224,7 +225,7 @@ public class IssueShowWsHandlerTest {
 
   @Test
   public void show_issue_with_actions() throws Exception {
-    Issue issue = createStandardIssue()
+    DefaultIssue issue = createStandardIssue()
       .setStatus("OPEN");
     issues.add(issue);
 
@@ -233,6 +234,44 @@ public class IssueShowWsHandlerTest {
     tester.execute("show", request).assertJson(getClass(), "show_issue_with_actions.json");
   }
 
+  @Test
+  public void show_issue_with_severity_action() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN");
+    issues.add(issue);
+
+    MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, issue.projectKey());
+    SimpleRequest request = new SimpleRequest().setParam("key", issue.key());
+    tester.execute("show", request).assertJson(getClass(), "show_issue_with_severity_action.json");
+  }
+
+  @Test
+  public void show_issue_with_assign_to_me_action() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN");
+    issues.add(issue);
+
+    MockUserSession.set().setLogin("john");
+    SimpleRequest request = new SimpleRequest().setParam("key", issue.key());
+    tester.execute("show", request).assertJson(getClass(), "show_issue_with_assign_to_me_action.json");
+  }
+
+  @Test
+  public void show_issue_without_assign_to_me_action() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN")
+      .setAssignee("john");
+    issues.add(issue);
+
+    result.addUsers(Lists.<User>newArrayList(
+      new DefaultUser().setLogin("john").setName("John")
+    ));
+
+    MockUserSession.set().setLogin("john");
+    SimpleRequest request = new SimpleRequest().setParam("key", issue.key());
+    tester.execute("show", request).assertJson(getClass(), "show_issue_without_assign_to_me_action.json");
+  }
+
   @Test
   public void show_issue_with_actions_defined_by_plugins() throws Exception {
     Issue issue = createStandardIssue()
index c7f76929102a6844fdc0ad1f5175addbcaf4ce85..8fe2518c8802fb7d406d227810ddbe872b499f0d 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.user;
 
 import com.google.common.collect.HashMultimap;
+import org.sonar.core.user.AuthorizationDao;
 
 import javax.annotation.Nullable;
 
@@ -28,6 +29,7 @@ import java.util.Collections;
 import java.util.Locale;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static org.mockito.Mockito.mock;
 
 public class MockUserSession extends UserSession {
 
@@ -76,4 +78,9 @@ public class MockUserSession extends UserSession {
     this.projectKeyByPermission.putAll(projectPermission, newArrayList(projectKeys));
     return this;
   }
+
+  @Override
+  AuthorizationDao authorizationDao() {
+    return mock(AuthorizationDao.class);
+  }
 }
index 689c9cf12b7798a33f4cb50b5a1c0b82657f9dfd..6db96acf67753b9d3e7b796331f84b8e8d986e86 100644 (file)
@@ -9,7 +9,7 @@
     "creationDate": "2014-01-22T19:10:03+0100",
     "transitions": [],
     "actions": [
-       "comment", "assign", "plan"
+       "comment", "assign", "assign_to_me", "plan"
     ],
     "comments": [],
     "changelog": []
index 7017792593bd99ec8b7ca98f3cc7af10df034cb9..ff0073874848903a25cf25608cfd517f2d4e77bb 100644 (file)
@@ -9,7 +9,7 @@
     "creationDate": "2014-01-22T19:10:03+0100",
     "transitions": [],
     "actions": [
-       "comment", "assign", "plan", "link-to-jira"
+       "comment", "assign", "assign_to_me", "plan", "link-to-jira"
     ],
     "comments": [],
     "changelog": []
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json
new file mode 100644 (file)
index 0000000..6db96ac
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "project": "org.sonar.Sonar",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "assign_to_me", "plan"
+    ],
+    "comments": [],
+    "changelog": []
+  }
+}
index 813b671846da17513193dbf252c28558ce74aff2..0018368eb785a9ec9a17965b31317c5f2a9fd08d 100644 (file)
@@ -7,7 +7,7 @@
     "ruleName": "Avoid cycle",
     "creationDate": "2014-01-22T19:10:03+0100",
     "transitions": [],
-    "actions": ["comment", "assign", "plan"],
+    "actions": ["comment", "assign", "assign_to_me", "plan"],
     "comments": [
       {
         "key": "COMMENT-ABCD",
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_severity_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_severity_action.json
new file mode 100644 (file)
index 0000000..56d968f
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "project": "org.sonar.Sonar",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "assign_to_me", "plan", "severity"
+    ],
+    "comments": [],
+    "changelog": []
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json
new file mode 100644 (file)
index 0000000..fc42c44
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "project": "org.sonar.Sonar",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "assignee": "john",
+    "assigneeName": "John",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "plan"
+    ],
+    "comments": [],
+    "changelog": []
+  }
+}
index d7ea45952c91f1c24e8b107c1f0c746ec6bef676..69c380f605e9dcc43c2aebb55cf044c6d370f989 100644 (file)
@@ -42,6 +42,14 @@ public interface IssueClient {
    */
   Issue assign(String issueKey, @Nullable String assignee);
 
+  /**
+   * Assign an existing issue to current user.
+   *
+   * @return the updated issue
+   */
+  Issue assignToMe(String issueKey);
+
+
   /**
    * Change the severity of an existing issue. Supported values are "INFO", "MINOR",
    * "MAJOR", "CRITICAL" and "BLOCKER".
index 95b4cf798face3d0e5f874822a110876b8c523cb..b9c5ae350afda4800cd295408503dc66e867d9df 100644 (file)
@@ -69,6 +69,13 @@ public class DefaultIssueClient implements IssueClient {
     return jsonToIssue(json);
   }
 
+  @Override
+  public Issue assignToMe(String issueKey) {
+    Map<String, Object> params = EncodingUtils.toMap("issue", issueKey, "me", "true");
+    String json = requestFactory.post("/api/issues/assign", params);
+    return jsonToIssue(json);
+  }
+
   @Override
   public Issue plan(String issueKey, @Nullable String actionPlanKey) {
     Map<String, Object> params = EncodingUtils.toMap("issue", issueKey, "plan", actionPlanKey);
index b01badc4bdc48b2b4bc1192e0eb276c259072097..983ed391e8de8a2ecbe22dfa9b745ce68961155e 100644 (file)
@@ -100,6 +100,22 @@ public class DefaultIssueClientTest {
     assertThat(result).isNotNull();
   }
 
+  @Test
+  public void assign_to_me() {
+    HttpRequestFactory requestFactory = new HttpRequestFactory(httpServer.url());
+    httpServer.stubResponseBody("{\"issue\": {\"key\": \"ABCDE\"}}");
+
+    IssueClient client = new DefaultIssueClient(requestFactory);
+    Issue result = client.assignToMe("ABCDE");
+
+    assertThat(httpServer.requestedPath()).isEqualTo("/api/issues/assign");
+    assertThat(httpServer.requestParams()).includes(
+      entry("issue", "ABCDE"),
+      entry("me", "true")
+    );
+    assertThat(result).isNotNull();
+  }
+
   @Test
   public void should_unassign() {
     HttpRequestFactory requestFactory = new HttpRequestFactory(httpServer.url());