Generalize search result parsing

This helps with deduplication. And it exposes the mismatch of
maniphestSearch's return type, which we fix accordingly to be able to
take advantage of the generalized search result parsing.

Change-Id: I827e6eebe2692363b8755a2685f520fbe8edb88d
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java
index 39c0ce5..205507f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java
@@ -16,13 +16,11 @@
 
 import com.google.common.collect.Sets;
 import com.google.gson.Gson;
-import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ConduitPing;
-import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.GenericSearch;
 import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ManiphestEdit;
 import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ManiphestSearch;
 import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ProjectSearch;
@@ -51,6 +49,7 @@
 
   public static final int CONDUIT_VERSION = 7;
 
+  private final SearchUtils searchUtils;
   private final ConduitConnection conduitConnection;
   private final Gson gson;
   private final String token;
@@ -58,8 +57,10 @@
   @Inject
   public Conduit(
       ConduitConnection.Factory conduitConnectionFactory,
+      SearchUtils searchUtils,
       @Assisted("baseUrl") String baseUrl,
       @Assisted("token") String token) {
+    this.searchUtils = searchUtils;
     this.conduitConnection = conduitConnectionFactory.create(baseUrl);
     this.token = token;
     this.gson = new Gson();
@@ -76,7 +77,7 @@
   }
 
   /** Runs the API's 'maniphest.search' method */
-  public GenericSearch maniphestSearch(int taskId) throws ConduitException {
+  public ManiphestSearch maniphestSearch(int taskId) throws ConduitException {
     HashMap<String, Object> params = new HashMap<>();
     HashMap<String, Object> params2 = new HashMap<>();
     HashMap<String, Object> params3 = new HashMap<>();
@@ -92,8 +93,10 @@
     params.put("attachments", params3);
 
     JsonElement callResult = conduitConnection.call("maniphest.search", params, token);
-    GenericSearch result = gson.fromJson(callResult, GenericSearch.class);
-    return result;
+    return searchUtils.stream(callResult, ManiphestSearch.class)
+        .filter((r) -> r.getId() == taskId)
+        .findFirst()
+        .orElse(null);
   }
 
   /** Runs the API's 'maniphest.edit' method */
@@ -114,20 +117,10 @@
 
       Set<String> projectPhids = Sets.newHashSet(projectPhid);
 
-      GenericSearch taskSearch = maniphestSearch(taskId);
-      JsonArray maniphestResultEntryValue = taskSearch.getData().getAsJsonArray();
-
-      for (JsonElement jsonElement : maniphestResultEntryValue) {
-        ManiphestSearch maniphestResultManiphestSearch =
-            gson.fromJson(jsonElement, ManiphestSearch.class);
-        for (JsonElement jsonElement2 :
-            maniphestResultManiphestSearch
-                .getAttachments()
-                .getProjects()
-                .getProjectPHIDs()
-                .getAsJsonArray()) {
-          projectPhids.add(jsonElement2.getAsString());
-        }
+      ManiphestSearch taskSearch = maniphestSearch(taskId);
+      for (JsonElement jsonElement2 :
+          taskSearch.getAttachments().getProjects().getProjectPHIDs().getAsJsonArray()) {
+        projectPhids.add(jsonElement2.getAsString());
       }
 
       maniphestEdit(taskId, projectPhids, actions);
@@ -182,16 +175,9 @@
     params.put("constraints", params2);
 
     JsonElement callResult = conduitConnection.call("project.search", params, token);
-    GenericSearch projectResult = gson.fromJson(callResult, GenericSearch.class);
-    JsonArray projectResultData = projectResult.getData().getAsJsonArray();
-
-    ProjectSearch result = null;
-    for (JsonElement jsonElement : projectResultData) {
-      ProjectSearch projectResultSearch = gson.fromJson(jsonElement, ProjectSearch.class);
-      if (projectResultSearch.getFields().getName().equals(name)) {
-        result = projectResultSearch;
-      }
-    }
-    return result;
+    return searchUtils.stream(callResult, ProjectSearch.class)
+        .filter((r) -> r.getFields().getName().equals(name))
+        .findFirst()
+        .orElse(null);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/SearchUtils.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/SearchUtils.java
new file mode 100644
index 0000000..0cfaf5c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/SearchUtils.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.phabricator.conduit;
+
+import com.google.common.collect.Streams;
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.GenericSearch;
+import java.util.stream.Stream;
+
+public class SearchUtils {
+  private final Gson gson;
+
+  @Inject
+  public SearchUtils() {
+    gson = new Gson();
+  }
+
+  public <T> Stream<T> stream(JsonElement jsonResult, Class<T> classOfT) {
+    GenericSearch result = gson.fromJson(jsonResult, GenericSearch.class);
+    return Streams.stream(result.getData()).map((json) -> gson.fromJson(json, classOfT));
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/GenericSearch.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/GenericSearch.java
index c1efd07..c911df8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/GenericSearch.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/GenericSearch.java
@@ -14,7 +14,7 @@
 
 package com.googlesource.gerrit.plugins.its.phabricator.conduit.results;
 
-import com.google.gson.JsonElement;
+import com.google.gson.JsonArray;
 
 /**
  * Models the result for API methods
@@ -40,9 +40,9 @@
  * </pre>
  */
 public class GenericSearch {
-  private JsonElement data;
+  private JsonArray data;
 
-  public JsonElement getData() {
+  public JsonArray getData() {
     return data;
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java
index f797e15..e8f701e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java
@@ -146,6 +146,6 @@
   }
 
   private Conduit createConduit() {
-    return new Conduit(conduitConnectionFactory, URL, TOKEN);
+    return new Conduit(conduitConnectionFactory, new SearchUtils(), URL, TOKEN);
   }
 }