Add ls-batches command - list batches visible to caller

Allows the user to list batches created with the batch plugin.

Change-Id: I2f6e19699d6e268fe2407a9f7a07de4ddbd3f6f8
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/Batch.java b/src/main/java/com/googlesource/gerrit/plugins/batch/Batch.java
index 40ff843..b2058ce 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/Batch.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/Batch.java
@@ -69,6 +69,11 @@
     this.owner = owner;
   }
 
+  /** Create a barebones entry for listing, not to build with */
+  public Batch(String id) {
+    this.id = id;
+  }
+
   /** Get a non-null list without modifying the batch. */
   public List<Destination> listDestinations() {
     if (destinations == null) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java b/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java
index 2d34784..749f8de 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.reviewdb.client.File;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.meta.GitFile;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.util.RefUpdater;
@@ -24,29 +25,48 @@
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.batch.exception.NoSuchBatchException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.ConcurrentModificationException;
 import java.util.Date;
+import java.util.List;
 import javax.inject.Singleton;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Repository;
 
 @Singleton
 public class BatchStore {
   public static final String BATCHES_REF = "refs/meta/batch/batches/";
   public static final String FILE_NAME = "batch.json";
 
+  protected final GitRepositoryManager repoManager;
   protected final Project.NameKey project;
   protected final GitFile.Factory gitFileFactory;
   protected final RefUpdater refUpdater;
   protected final Gson gson = new Gson();
 
   @Inject
-  public BatchStore(AllProjectsName project, GitFile.Factory gitFileFactory, RefUpdater refUpdater)
-      throws IOException {
+  public BatchStore(
+      GitRepositoryManager repoManager,
+      AllProjectsName project,
+      GitFile.Factory gitFileFactory,
+      RefUpdater refUpdater) {
+    this.repoManager = repoManager;
     this.project = project;
     this.gitFileFactory = gitFileFactory;
     this.refUpdater = refUpdater;
   }
 
+  /** Returns barebones batch objects for listings */
+  public List<Batch> find() throws IOException {
+    List<Batch> batches = new ArrayList<>();
+    try (Repository repo = repoManager.openRepository(project)) {
+      for (String batchId : repo.getRefDatabase().getRefs(BATCHES_REF).keySet()) {
+        batches.add(new Batch(batchId));
+      }
+    }
+    return batches;
+  }
+
   public void save(Batch batch) throws IOException, NoSuchProjectException {
     if (batch.state == Batch.State.DELETED) {
       refUpdater.delete(getBranch(batch.id));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java
new file mode 100644
index 0000000..4757f89
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 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.batch;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.server.OutputFormat;
+import com.google.gson.reflect.TypeToken;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+public class ListBatches {
+  protected final BatchStore store;
+
+  @Inject
+  ListBatches(BatchStore store) {
+    this.store = store;
+  }
+
+  public void display(OutputStream displayOutputStream) throws IOException, OrmException {
+    try {
+      PrintWriter stdout =
+          new PrintWriter(new BufferedWriter(new OutputStreamWriter(displayOutputStream, UTF_8)));
+      try {
+        OutputFormat.JSON
+            .newGson()
+            .toJson(getBatches(), new TypeToken<List<Batch>>() {}.getType(), stdout);
+        stdout.print('\n');
+      } finally {
+        stdout.flush();
+      }
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException("JVM lacks UTF-8 encoding", e);
+    }
+  }
+
+  public List<Batch> getBatches() throws IOException {
+    return store.find();
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/ListCommand.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/ListCommand.java
new file mode 100644
index 0000000..6eef5aa
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/ListCommand.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2016 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.batch.ssh;
+
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.gerrit.sshd.CommandMetaData;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.batch.ListBatches;
+import org.apache.sshd.server.Environment;
+
+@CommandMetaData(name = "ls-batches", description = "List batches visible to caller")
+public class ListCommand extends BaseCommand {
+  protected final ListBatches impl;
+
+  @Inject
+  ListCommand(ListBatches impl) {
+    this.impl = impl;
+  }
+
+  @Override
+  public void start(final Environment env) {
+    startThread(
+        new CommandRunnable() {
+          @Override
+          public void run() throws Exception {
+            parseCommandLine(impl);
+            impl.display(out);
+          }
+        });
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SshModule.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SshModule.java
index 6c7d22d..2fd7b81 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SshModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SshModule.java
@@ -24,5 +24,6 @@
 
     command(MergeChangeCommand.class);
     command(DeleteCommand.class);
+    command(ListCommand.class);
   }
 }
diff --git a/src/main/resources/Documentation/cmd-ls-batches.md b/src/main/resources/Documentation/cmd-ls-batches.md
new file mode 100644
index 0000000..45947cb
--- /dev/null
+++ b/src/main/resources/Documentation/cmd-ls-batches.md
@@ -0,0 +1,42 @@
+@PLUGIN@ ls-batches
+=====================
+
+NAME
+----
+@PLUGIN@ ls-batches - List batches visible to caller
+
+SYNOPSIS
+--------
+```
+ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ ls-batches
+```
+
+DESCRIPTION
+-----------
+Displays the list of batches, in JSON.
+
+ACCESS
+------
+Any user who has configured an SSH key, and is a member
+of the privileged *Administrators* group.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+EXAMPLES
+--------
+List visible batches:
+
+```
+  ssh -p 29418 review.example.com batch ls-batches
+  [
+    {
+      "id": "c2a24c5a-dbb4-43f9-bec7-d999cc7b3f53"
+    },
+    ...,
+    {
+      "id": "42aa58ae-85e9-4e46-abd9-c82391b4222c"
+    }
+  ]
+```
diff --git a/test/test_batch_merge.sh b/test/test_batch_merge.sh
index 50ee448..223d9e0 100755
--- a/test/test_batch_merge.sh
+++ b/test/test_batch_merge.sh
@@ -214,6 +214,15 @@
 result "$GROUP retry" "$delete"
 
 
+setupGroup "ls-batches" "List Batches" # -------------
+
+ch1=$(create_change "$REF_BRANCH" "$FILE_A") || exit
+bjson=$(batchssh merge-change --close "$ch1",1)
+list=$(batchssh ls-batches)
+echo "$list"| grep '"id"'| grep -q "$(b_id)"
+result "$GROUP" "$list"
+
+
 setupGroup "independent clean" "Independent changes, clean merge" # ------------
 
 ch1=$(create_change "$REF_BRANCH" "$FILE_A") || exit