diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-01-23 14:14:04 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-01-23 14:14:04 +0100 |
commit | 9a71693b65cf1357b354f123348738ea9ac3b61b (patch) | |
tree | 80cc9414fedac224e87617014efad3c5b2673747 | |
parent | 0583f0b2e02c2dd1c6f32c3063ed9716af972eac (diff) | |
download | sonarqube-9a71693b65cf1357b354f123348738ea9ac3b61b.tar.gz sonarqube-9a71693b65cf1357b354f123348738ea9ac3b61b.zip |
IssueShowWS : Add assign to me action
18 files changed, 192 insertions, 10 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java b/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java index a79aead0e47..ee9682ea820 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java @@ -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(); diff --git a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index f4b3b280064..63da1651fbf 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -20,9 +20,11 @@ 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 { diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java index 02fb37c8ee6..c7c5df9e7e3 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java @@ -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()); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb index 3f5f412f6d4..fc391b6c168 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb @@ -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 diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb index fc552a538eb..44204a77e32 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb @@ -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') -%>', @@ -55,6 +57,18 @@ 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') -%>', } }); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java index be2161dfbdc..325a096174d 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java @@ -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) { diff --git a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java index 08ef61282d0..b9dec4a89a8 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java @@ -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"); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java index 5ff02ef0273..c0041331d3e 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java @@ -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); @@ -234,6 +235,44 @@ public class IssueShowWsHandlerTest { } @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() .setStatus("OPEN"); diff --git a/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java b/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java index c7f76929102..8fe2518c880 100644 --- a/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java +++ b/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java @@ -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); + } } diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json index 689c9cf12b7..6db96acf677 100644 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json @@ -9,7 +9,7 @@ "creationDate": "2014-01-22T19:10:03+0100", "transitions": [], "actions": [ - "comment", "assign", "plan" + "comment", "assign", "assign_to_me", "plan" ], "comments": [], "changelog": [] diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json index 7017792593b..ff007387484 100644 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json @@ -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 index 00000000000..6db96acf677 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json @@ -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": [] + } +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json index 813b671846d..0018368eb78 100644 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json @@ -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 index 00000000000..56d968f7682 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_severity_action.json @@ -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 index 00000000000..fc42c4420a3 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json @@ -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": [] + } +} diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueClient.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueClient.java index d7ea45952c9..69c380f605e 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueClient.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueClient.java @@ -43,6 +43,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". * diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueClient.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueClient.java index 95b4cf798fa..b9c5ae350af 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueClient.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueClient.java @@ -70,6 +70,13 @@ public class DefaultIssueClient implements IssueClient { } @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); String json = requestFactory.post("/api/issues/plan", params); diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/DefaultIssueClientTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/DefaultIssueClientTest.java index b01badc4bdc..983ed391e8d 100644 --- a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/DefaultIssueClientTest.java +++ b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/DefaultIssueClientTest.java @@ -101,6 +101,22 @@ public class DefaultIssueClientTest { } @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()); httpServer.stubResponseBody("{\"issue\": {\"key\": \"ABCDE\"}}"); |