Revision API: Implement methods to get / set review status

Change-Id: If68c43e94acdfcffd7b41e78179f5b2e3f83db48
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
index cc19c15..32705ba 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -56,7 +56,7 @@
 
 public class PushOneCommit {
   public static final String SUBJECT = "test commit";
-  static final String FILE_NAME = "a.txt";
+  public static final String FILE_NAME = "a.txt";
   private static final String FILE_CONTENT = "some content";
 
   public interface Factory {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 78b9a54..d77f11f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -14,9 +14,11 @@
 
 package com.google.gerrit.acceptance.api.revision;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
@@ -169,6 +171,36 @@
         .canRebase());
   }
 
+  @Test
+  public void setUnsetReviewedFlag() throws Exception {
+    PushOneCommit push = pushFactory.create(db, admin.getIdent());
+    PushOneCommit.Result r = push.to(git, "refs/for/master");
+
+    gApi.changes()
+        .id(r.getChangeId())
+        .current()
+        .setReviewed(PushOneCommit.FILE_NAME, true);
+
+    assertEquals(PushOneCommit.FILE_NAME,
+        Iterables.getOnlyElement(
+            gApi.changes()
+                .id(r.getChangeId())
+                .current()
+                .reviewed()));
+
+    gApi.changes()
+        .id(r.getChangeId())
+        .current()
+        .setReviewed(PushOneCommit.FILE_NAME, false);
+
+    assertTrue(
+        gApi.changes()
+            .id(r.getChangeId())
+            .current()
+            .reviewed()
+            .isEmpty());
+  }
+
   protected RevisionApi revision(PushOneCommit.Result r) throws Exception {
     return gApi.changes()
         .id(r.getChangeId())
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 4b84cab..c709b34 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.api.changes;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.ChangeApi;
 import com.google.gerrit.extensions.api.changes.Changes;
@@ -21,12 +22,17 @@
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
+import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.CherryPick;
 import com.google.gerrit.server.change.DeleteDraftPatchSet;
+import com.google.gerrit.server.change.FileResource;
+import com.google.gerrit.server.change.Files;
 import com.google.gerrit.server.change.PostReview;
 import com.google.gerrit.server.change.Publish;
 import com.google.gerrit.server.change.Rebase;
+import com.google.gerrit.server.change.Reviewed;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.Submit;
 import com.google.gerrit.server.changedetail.RebaseChange;
@@ -36,6 +42,7 @@
 import com.google.inject.assistedinject.Assisted;
 
 import java.io.IOException;
+import java.util.Set;
 
 class RevisionApiImpl extends RevisionApi.NotImplemented implements RevisionApi {
   interface Factory {
@@ -49,7 +56,11 @@
   private final RebaseChange rebaseChange;
   private final Submit submit;
   private final Publish publish;
+  private final Reviewed.PutReviewed putReviewed;
+  private final Reviewed.DeleteReviewed deleteReviewed;
   private final RevisionResource revision;
+  private final Provider<Files> files;
+  private final Provider<Files.ListFiles> listFiles;
   private final Provider<PostReview> review;
 
   @Inject
@@ -60,6 +71,10 @@
       RebaseChange rebaseChange,
       Submit submit,
       Publish publish,
+      Reviewed.PutReviewed putReviewed,
+      Reviewed.DeleteReviewed deleteReviewed,
+      Provider<Files> files,
+      Provider<Files.ListFiles> listFiles,
       Provider<PostReview> review,
       @Assisted RevisionResource r) {
     this.changes = changes;
@@ -70,6 +85,10 @@
     this.review = review;
     this.submit = submit;
     this.publish = publish;
+    this.files = files;
+    this.putReviewed = putReviewed;
+    this.deleteReviewed = deleteReviewed;
+    this.listFiles = listFiles;
     this.revision = r;
   }
 
@@ -138,4 +157,33 @@
       throw new RestApiException("Cannot cherry pick", e);
     }
   }
+
+  @Override
+  public void setReviewed(String path, boolean reviewed) throws RestApiException {
+    try {
+      RestModifyView<FileResource, Reviewed.Input> view;
+      if (reviewed) {
+        view = putReviewed;
+      } else {
+        view = deleteReviewed;
+      }
+      view.apply(
+          files.get().parse(revision, IdString.fromDecoded(path)),
+          new Reviewed.Input());
+    } catch (Exception e) {
+      throw new RestApiException("Cannot update reviewed flag", e);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public Set<String> reviewed() throws RestApiException {
+    try {
+      return ImmutableSet.copyOf((Iterable<String>) listFiles
+          .get().setReviewed(true)
+          .apply(revision).value());
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot list reviewed files", e);
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
index 75b9901..712c196 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
@@ -59,7 +59,7 @@
 import java.util.SortedSet;
 import java.util.concurrent.TimeUnit;
 
-class Files implements ChildCollection<RevisionResource, FileResource> {
+public class Files implements ChildCollection<RevisionResource, FileResource> {
   private final DynamicMap<RestView<FileResource>> views;
   private final Provider<ListFiles> list;
 
@@ -85,7 +85,7 @@
     return new FileResource(rev, id.get());
   }
 
-  private static final class ListFiles implements RestReadView<RevisionResource> {
+  public static final class ListFiles implements RestReadView<RevisionResource> {
     private static final Logger log = LoggerFactory.getLogger(ListFiles.class);
 
     @Option(name = "--base", metaVar = "revision-id")
@@ -116,6 +116,11 @@
       this.patchListCache = patchListCache;
     }
 
+    public ListFiles setReviewed(boolean r) {
+      this.reviewed = r;
+      return this;
+    }
+
     @Override
     public Response<?> apply(RevisionResource resource) throws AuthException,
         BadRequestException, ResourceNotFoundException, OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
index c138a60..120a414 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
@@ -26,12 +26,12 @@
 
 import java.util.Collections;
 
-class Reviewed {
-  static class Input {
+public class Reviewed {
+  public static class Input {
   }
 
   @Singleton
-  static class PutReviewed implements RestModifyView<FileResource, Input> {
+  public static class PutReviewed implements RestModifyView<FileResource, Input> {
     private final Provider<ReviewDb> dbProvider;
 
     @Inject
@@ -60,7 +60,7 @@
   }
 
   @Singleton
-  static class DeleteReviewed implements RestModifyView<FileResource, Input> {
+  public static class DeleteReviewed implements RestModifyView<FileResource, Input> {
     private final Provider<ReviewDb> dbProvider;
 
     @Inject