@Override
public boolean execute(Map<String, Object> properties, Context context) {
- context.issueUpdater().assign((DefaultIssue) context.issue(), assignee(properties), context.issueChangeContext());
- return true;
+ return context.issueUpdater().assign((DefaultIssue) context.issue(), assignee(properties), context.issueChangeContext());
}
private String assignee(Map<String, Object> properties){
public class IssueBulkChangeResult {
- List<Issue> issuesChanged = newArrayList();
- List<Issue> issuesNotChanged = newArrayList();
+ private List<Issue> issuesChanged = newArrayList();
+ private List<Issue> issuesNotChanged = newArrayList();
public void addIssueChanged(Issue issue){
this.issuesChanged.add(issue);
return issuesChanged;
}
- public List<Issue> issuesInError() {
+ public List<Issue> issuesNotChanged() {
return issuesNotChanged;
}
}
@Override
public boolean execute(Map<String, Object> properties, Context context) {
- context.issueUpdater().plan((DefaultIssue) context.issue(), planKey(properties), context.issueChangeContext());
- return true;
+ return context.issueUpdater().plan((DefaultIssue) context.issue(), planKey(properties), context.issueChangeContext());
}
private String planKey(Map<String, Object> properties) {
@Override
public boolean execute(Map<String, Object> properties, Context context) {
- context.issueUpdater().setSeverity((DefaultIssue) context.issue(), severity(properties), context.issueChangeContext());
- return true;
+ return context.issueUpdater().setSeverity((DefaultIssue) context.issue(), severity(properties), context.issueChangeContext());
}
private String severity(Map<String, Object> properties) {
render_result_issue(result)
end
+ #
+ # Execute a bulk change on a list of issues
+ #
+ # POST /api/issues/bulk_change?issue=<key>&text=<text>
+ #
+ # -- Mandatory parameters
+ # 'issues' is the list of issue keys
+ #
+ # -- Optional parameters
+ # 'assignee' to assign all issues to a user or un-assign.
+ # 'severity' to change the severity of all issues.
+ # 'plan' to plan all issues to an action plan or unlink.
+ # 'transition' to execute a transition on all issues.
+ # 'comment' to add a comment on all issues.
+ #
+ # -- Example
+ # curl -X POST -v -u admin:admin 'http://localhost:9000/api/issues/bulk_change?issues=4a2881e7-825e-4140-a154-01f420c43d11,4a2881e7-825e-4140-a154-01f420c43d30&assignee=simon&plan=3.7'
+ #
+ def bulk_change
+ verify_post_request
+
+ comment = Api::Utils.read_post_request_param(params[:comment])
+ result = Internal.issues.bulkChange(params, comment)
+ hash = result_to_hash(result)
+ if result.get
+ hash[:issuesChanged] = {
+ :total => result.get.issuesChanged().size,
+ }
+ hash[:issuesNotChanged] = {
+ :total => result.get.issuesNotChanged().size,
+ :issues => result.get.issuesNotChanged().map { |issue| issue.key() }
+ }
+ end
+
+ respond_to do |format|
+ # if the request header "Accept" is "*/*", then the default format is the first one (json)
+ format.json { render :json => jsonp(hash), :status => result.httpStatus }
+ format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'sonar', :status => (result.ok ? 200 : 400)) }
+ end
+ end
+
protected
def render_result_issue(result)
- http_status = (result.ok ? 200 : 400)
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 => jsonp(hash), :status => result.httpStatus }
- format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'sonar', :status => http_status) }
+ format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'sonar', :status => (result.ok ? 200 : 400)) }
end
end
IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties);
IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession);
- assertThat(result.issuesChanged).hasSize(1);
- assertThat(result.issuesNotChanged).isEmpty();
+ assertThat(result.issuesChanged()).hasSize(1);
+ assertThat(result.issuesNotChanged()).isEmpty();
verifyNoMoreInteractions(issueUpdater);
verify(issueStorage).save(eq(issue));
IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties);
IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession);
- assertThat(result.issuesChanged).isEmpty();
- assertThat(result.issuesNotChanged).hasSize(1);
+ assertThat(result.issuesChanged()).isEmpty();
+ assertThat(result.issuesNotChanged()).hasSize(1);
verifyZeroInteractions(issueUpdater);
verifyZeroInteractions(issueStorage);
IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties);
IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession);
- assertThat(result.issuesChanged).isEmpty();
- assertThat(result.issuesNotChanged).hasSize(1);
+ assertThat(result.issuesChanged()).isEmpty();
+ assertThat(result.issuesNotChanged()).hasSize(1);
verifyZeroInteractions(issueUpdater);
verifyZeroInteractions(issueStorage);
IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties);
IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession);
- assertThat(result.issuesChanged).isEmpty();
- assertThat(result.issuesNotChanged).hasSize(1);
+ assertThat(result.issuesChanged()).isEmpty();
+ assertThat(result.issuesNotChanged()).hasSize(1);
verifyZeroInteractions(issueUpdater);
verifyZeroInteractions(issueStorage);
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.wsclient.issue;
+
+import java.util.List;
+
+/**
+ * @since 3.7
+ */
+public interface BulkChange {
+
+ List<String> issuesNotChangedKeys();
+
+ int totalIssuesChanged();
+
+ int totalIssuesNotChanged();
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.wsclient.issue;
+
+import org.sonar.wsclient.internal.EncodingUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @since 3.7
+ */
+public class BulkChangeQuery {
+
+ private final Map<String, Object> params = new HashMap<String, Object>();
+
+ private BulkChangeQuery() {
+ }
+
+ public static BulkChangeQuery create() {
+ return new BulkChangeQuery();
+ }
+
+ /**
+ * URL query string, for internal use
+ */
+ public Map<String, Object> urlParams() {
+ return params;
+ }
+
+ public BulkChangeQuery issues(String... keys) {
+ return addParam("issues", keys);
+ }
+
+ public BulkChangeQuery actions(String... actions) {
+ return addParam("actions", actions);
+ }
+
+ public BulkChangeQuery actionParameter(String action, String parameter, Object value) {
+ params.put(action + "." + parameter, value);
+ return this;
+ }
+
+ public BulkChangeQuery comment(String comment) {
+ params.put("comment", comment);
+ return this;
+ }
+
+ private BulkChangeQuery addParam(String key, String[] values) {
+ if (values != null) {
+ params.put(key, EncodingUtils.toQueryParam(values));
+ }
+ return this;
+ }
+
+}
Issue doAction(String issueKey, String action);
+ /**
+ * Execute bulk change on a list of issues
+ */
+ BulkChange bulkChange(BulkChangeQuery query);
+
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.wsclient.issue.internal;
+
+import org.sonar.wsclient.issue.BulkChange;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @since 3.7
+ */
+public class DefaultBulkChange implements BulkChange {
+
+ private int totalIssuesChanged;
+ private int totalIssuesNotChanged;
+ private final List<String> issuesNotChangedKeys = new ArrayList<String>();
+
+ public List<String> issuesNotChangedKeys() {
+ return issuesNotChangedKeys;
+ }
+
+ public int totalIssuesChanged() {
+ return totalIssuesChanged;
+ }
+
+ public int totalIssuesNotChanged() {
+ return totalIssuesNotChanged;
+ }
+
+ DefaultBulkChange setTotalIssuesChanged(int totalIssuesChanged) {
+ this.totalIssuesChanged = totalIssuesChanged;
+ return this;
+ }
+
+ DefaultBulkChange setTotalIssuesNotChanged(int totalIssuesNotChanged) {
+ this.totalIssuesNotChanged = totalIssuesNotChanged;
+ return this;
+ }
+
+ DefaultBulkChange setIssuesNotChanged(List<String> issueKeys) {
+ issuesNotChangedKeys.addAll(issueKeys);
+ return this;
+ }
+
+ DefaultBulkChange addIssueNotchanged(String issueKey) {
+ issuesNotChangedKeys.add(issueKey);
+ return this;
+ }
+
+}
import org.sonar.wsclient.issue.*;
import javax.annotation.Nullable;
+
import java.util.List;
import java.util.Map;
return jsonToIssue(json);
}
+ @Override
+ public BulkChange bulkChange(BulkChangeQuery query) {
+ String json = requestFactory.post("/api/issues/bulk_change", query.urlParams());
+ return parser.parseBulkChange(json);
+ }
+
private Issue jsonToIssue(String json) {
Map jsonRoot = (Map) JSONValue.parse(json);
return new DefaultIssue((Map) jsonRoot.get("issue"));
package org.sonar.wsclient.issue.internal;
import org.json.simple.JSONValue;
+import org.sonar.wsclient.base.Paging;
import org.sonar.wsclient.component.Component;
+import org.sonar.wsclient.issue.BulkChange;
import org.sonar.wsclient.issue.Issues;
-import org.sonar.wsclient.base.Paging;
import org.sonar.wsclient.rule.Rule;
import org.sonar.wsclient.unmarshallers.JsonUtils;
import org.sonar.wsclient.user.User;
}
return actions;
}
+
+ BulkChange parseBulkChange(String json) {
+ DefaultBulkChange result = new DefaultBulkChange();
+
+ Map jsonRoot = (Map) JSONValue.parse(json);
+ Map issuesChanged = (Map) jsonRoot.get("issuesChanged");
+ result.setTotalIssuesChanged(JsonUtils.getInteger(issuesChanged, "total"));
+
+ Map issuesNotChanged = (Map) jsonRoot.get("issuesNotChanged");
+ result.setTotalIssuesNotChanged(JsonUtils.getInteger(issuesNotChanged, "total"));
+ result.setIssuesNotChanged(JsonUtils.getArray(issuesNotChanged, "issues"));
+
+ return result;
+ }
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.wsclient.issue;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.MapAssert.entry;
+
+public class BulkChangeQueryTest {
+
+ @Test
+ public void get_all_issues() {
+ BulkChangeQuery query = BulkChangeQuery.create();
+ assertThat(query.urlParams()).isEmpty();
+ }
+
+ @Test
+ public void test_create() {
+ BulkChangeQuery query = BulkChangeQuery.create()
+ .issues("ABCD", "EFGH")
+ .actions("assign")
+ .actionParameter("assign", "assignee", "geoffrey")
+ ;
+
+ assertThat(query.urlParams()).hasSize(3).includes(
+ entry("issues", "ABCD,EFGH"),
+ entry("actions", "assign"),
+ entry("assign.assignee", "geoffrey")
+ );
+ }
+
+}
assertThat(httpServer.requestedPath()).isEqualTo("/api/issues/do_action?issue=ABCDE&actionKey=tweet");
assertThat(result).isNotNull();
}
+
+ @Test
+ public void should_do_bulk_change() {
+ HttpRequestFactory requestFactory = new HttpRequestFactory(httpServer.url());
+ httpServer.stubResponseBody("{\"issuesChanged\": {\"total\": 2}, \"issuesNotChanged\": {\"total\": 1, \"issues\": [\"06ed4db6-fd96-450a-bcb0-e0184db50105\"]} }");
+
+ BulkChangeQuery query = BulkChangeQuery.create()
+ .issues("ABCD", "EFGH")
+ .actions("assign")
+ .actionParameter("assign", "assignee", "geoffrey");
+
+ IssueClient client = new DefaultIssueClient(requestFactory);
+ BulkChange result = client.bulkChange(query);
+
+ assertThat(httpServer.requestedPath()).isEqualTo("/api/issues/bulk_change?assign.assignee=geoffrey&issues=ABCD,EFGH&actions=assign");
+ assertThat(result).isNotNull();
+ }
}
import org.junit.Test;
import org.sonar.wsclient.base.Paging;
import org.sonar.wsclient.component.Component;
-import org.sonar.wsclient.issue.ActionPlan;
-import org.sonar.wsclient.issue.Issue;
-import org.sonar.wsclient.issue.IssueComment;
-import org.sonar.wsclient.issue.Issues;
-import org.sonar.wsclient.issue.internal.IssueJsonParser;
+import org.sonar.wsclient.issue.*;
import org.sonar.wsclient.user.User;
import java.util.List;
assertThat(actionPlan.createdAt().getTime()).isEqualTo(1369828520000l);
assertThat(actionPlan.updatedAt().getTime()).isEqualTo(1369828520000l);
}
+
+ @Test
+ public void should_parse_bulk_change() throws Exception {
+ String json = IOUtils.toString(getClass().getResourceAsStream("/org/sonar/wsclient/issue/internal/IssueJsonParserTest/bulk-change.json"));
+ BulkChange bulkChange = new IssueJsonParser().parseBulkChange(json);
+
+ assertThat(bulkChange.totalIssuesChanged()).isEqualTo(3);
+ assertThat(bulkChange.totalIssuesNotChanged()).isEqualTo(2);
+ assertThat(bulkChange.issuesNotChangedKeys()).containsOnly("06ed4db6-fd96-450a-bcb0-e0184db50105", "06ed4db6-fd96-450a-bcb0-e0184db50654");
+ }
}
--- /dev/null
+{
+ "issuesChanged": {
+ "total": 3
+ },
+ "issuesNotChanged": {
+ "total": 2,
+ "issues": ["06ed4db6-fd96-450a-bcb0-e0184db50105", "06ed4db6-fd96-450a-bcb0-e0184db50654"]
+ }
+}
\ No newline at end of file