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();
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;
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.
*
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 {
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;
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);
}
// 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());
}
#
# 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
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
'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: {
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') -%>',
}
});
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) {
}
@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");
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;
@Test
public void show_issue_with_transitions() throws Exception {
- Issue issue = createStandardIssue()
+ DefaultIssue issue = createStandardIssue()
.setStatus("RESOLVED")
.setResolution("FIXED");
issues.add(issue);
@Test
public void show_issue_with_actions() throws Exception {
- Issue issue = createStandardIssue()
+ DefaultIssue issue = createStandardIssue()
.setStatus("OPEN");
issues.add(issue);
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()
package org.sonar.server.user;
import com.google.common.collect.HashMultimap;
+import org.sonar.core.user.AuthorizationDao;
import javax.annotation.Nullable;
import java.util.Locale;
import static com.google.common.collect.Lists.newArrayList;
+import static org.mockito.Mockito.mock;
public class MockUserSession extends UserSession {
this.projectKeyByPermission.putAll(projectPermission, newArrayList(projectKeys));
return this;
}
+
+ @Override
+ AuthorizationDao authorizationDao() {
+ return mock(AuthorizationDao.class);
+ }
}
"creationDate": "2014-01-22T19:10:03+0100",
"transitions": [],
"actions": [
- "comment", "assign", "plan"
+ "comment", "assign", "assign_to_me", "plan"
],
"comments": [],
"changelog": []
"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": []
--- /dev/null
+{
+ "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": []
+ }
+}
"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",
--- /dev/null
+{
+ "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": []
+ }
+}
--- /dev/null
+{
+ "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": []
+ }
+}
*/
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".
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);
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());