]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4420 New web service to bulk update issues
authorJulien Lancelot <julien.lancelot@gmail.com>
Tue, 25 Jun 2013 12:12:27 +0000 (14:12 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Tue, 25 Jun 2013 12:12:27 +0000 (14:12 +0200)
16 files changed:
sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java
sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeResult.java
sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java
sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb
sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Bulkchange.java [new file with mode: 0644]
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/BulkchangeQuery.java [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/DefaultBulkChange.java [new file with mode: 0644]
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueClient.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/IssueJsonParser.java
sonar-ws-client/src/test/java/org/sonar/wsclient/issue/BulkChangeQueryTest.java [new file with mode: 0644]
sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/DefaultIssueClientTest.java
sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java
sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/bulk-change.json [new file with mode: 0644]

index 15b35697b3c8b14451b07da7cc834902a04d5535..5fa96a2a37aa55d3034db1f1c6c47c3b199b1927 100644 (file)
@@ -54,8 +54,7 @@ public class AssignAction extends Action implements ServerComponent {
 
   @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){
index 092f3bfc4bb507b269fb8cf04075c8779ce00e1a..67d764ae0423fe65c7a371212d9f39e4798916b5 100644 (file)
@@ -28,8 +28,8 @@ import static com.google.common.collect.Lists.newArrayList;
 
 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);
@@ -43,7 +43,7 @@ public class IssueBulkChangeResult {
     return issuesChanged;
   }
 
-  public List<Issue> issuesInError() {
+  public List<Issue> issuesNotChanged() {
     return issuesNotChanged;
   }
 }
index 7c38b6c18c154d86e0ea8ac98eb7f1c00d169e4d..bb5363dfce1bee635ca9645811e92019c504c4d0 100644 (file)
@@ -61,8 +61,7 @@ public class PlanAction extends Action implements ServerComponent {
 
   @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) {
index e51501599e4fde230d02b16baea34bd82b7cabbb..1933d055d49917f84333c403303bead1215a1986 100644 (file)
@@ -46,8 +46,7 @@ public class SetSeverityAction extends Action implements ServerComponent {
 
   @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) {
index 7d21da4eb0db106e56406c9984df12f1dd8fea0b..b09e2e6db7b7827b096f6a1899dd16b8ce877dd0 100644 (file)
@@ -250,18 +250,58 @@ class Api::IssuesController < Api::ApiController
     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
 
index b2a9551d30db22dd804252918a9b9e857e39d7e3..64fba1ec89f1dae2cb081efb5c46cfe4f777d94e 100644 (file)
@@ -89,8 +89,8 @@ public class IssueBulkChangeServiceTest {
 
     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));
@@ -110,8 +110,8 @@ public class IssueBulkChangeServiceTest {
 
     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);
@@ -130,8 +130,8 @@ public class IssueBulkChangeServiceTest {
 
     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);
@@ -150,8 +150,8 @@ public class IssueBulkChangeServiceTest {
 
     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);
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Bulkchange.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Bulkchange.java
new file mode 100644 (file)
index 0000000..d20dd22
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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();
+
+}
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/BulkchangeQuery.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/BulkchangeQuery.java
new file mode 100644 (file)
index 0000000..b6980df
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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;
+  }
+
+}
index 1e1fd52df4fa7829abc46b3c4a16a751d98446a0..b5f26595474701090deceac0645936bc8eb82b18 100644 (file)
@@ -67,4 +67,9 @@ public interface IssueClient {
 
   Issue doAction(String issueKey, String action);
 
+  /**
+   * Execute bulk change on a list of issues
+   */
+  BulkChange bulkChange(BulkChangeQuery query);
+
 }
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultBulkChange.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultBulkChange.java
new file mode 100644 (file)
index 0000000..4671d89
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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;
+  }
+
+}
index c4b2f326555c4705959113f189e5aacb65996deb..6e8c60c8c14271c97289e94f96fb57ad611272a0 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.wsclient.internal.HttpRequestFactory;
 import org.sonar.wsclient.issue.*;
 
 import javax.annotation.Nullable;
+
 import java.util.List;
 import java.util.Map;
 
@@ -111,6 +112,12 @@ public class DefaultIssueClient implements IssueClient {
     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"));
index d123e9779cbb61f11babacb8b1d8d882f2c7107f..706267021d42c2cd5251d99bae1afd5160e308f3 100644 (file)
 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;
@@ -124,4 +125,18 @@ public class IssueJsonParser {
     }
     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;
+  }
 }
diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/BulkChangeQueryTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/BulkChangeQueryTest.java
new file mode 100644 (file)
index 0000000..cd756e7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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")
+    );
+  }
+
+}
index 9ad9f130e53bdf1e9cf7af229c50a59f28030c97..9befff8294fda5038f1d5db80b98d9d5d99e225f 100644 (file)
@@ -213,4 +213,21 @@ public class DefaultIssueClientTest {
     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();
+  }
 }
index c84bfe8c001e7889f52730559753dce927d6a679..211bfe0723dd4132a2f769a4213ed35225f4b357 100644 (file)
@@ -23,11 +23,7 @@ import org.apache.commons.io.IOUtils;
 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;
@@ -196,4 +192,14 @@ public class IssueJsonParserTest {
     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");
+  }
 }
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/bulk-change.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/bulk-change.json
new file mode 100644 (file)
index 0000000..9180ebf
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "issuesChanged": {
+    "total": 3
+  },
+  "issuesNotChanged": {
+    "total": 2,
+    "issues": ["06ed4db6-fd96-450a-bcb0-e0184db50105", "06ed4db6-fd96-450a-bcb0-e0184db50654"]
+  }
+}
\ No newline at end of file