Include addReviewer in ChangeApi.

Also, update the PostReviewers.Input to be named AddReviewerInput.

Change-Id: Ide60a3f261d59a3af988d94c3c1bdbd1dcfdcd79
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index dc65470..3c93c76 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.acceptance.git.PushOneCommit;
 import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -61,12 +62,15 @@
   private IdentifiedUser.GenericFactory identifiedUserFactory;
 
   private TestAccount admin;
+  private TestAccount user;
+
   private Git git;
   private ReviewDb db;
 
   @Before
   public void setUp() throws Exception {
     admin = accounts.admin();
+    user = accounts.user();
     initSsh(admin);
     Project.NameKey project = new Project.NameKey("p");
     SshSession sshSession = new SshSession(server, admin);
@@ -131,6 +135,17 @@
         .rebase();
   }
 
+  @Test
+  public void addReviewer() throws GitAPIException,
+      IOException, RestApiException {
+    PushOneCommit.Result r = createChange();
+    AddReviewerInput in = new AddReviewerInput();
+    in.reviewer = user.email;
+    gApi.changes()
+        .id("p~master~" + r.getChangeId())
+        .addReviewer(in);
+  }
+
   private PushOneCommit.Result createChange() throws GitAPIException,
       IOException {
     PushOneCommit push = new PushOneCommit(db, admin.getIdent());
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/AddReviewerInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/AddReviewerInput.java
new file mode 100644
index 0000000..30a23bf
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/AddReviewerInput.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2013 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.google.gerrit.extensions.api.changes;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class AddReviewerInput {
+  @DefaultInput
+  public String reviewer;
+  public Boolean confirmed;
+
+  public boolean confirmed() {
+    return (confirmed != null) ? confirmed : false;
+  }
+}
+
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index a264009..9aa9d48 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -31,4 +31,6 @@
 
   ChangeApi revert() throws RestApiException;
   ChangeApi revert(RevertInput in) throws RestApiException;
+
+  void addReviewer(AddReviewerInput in) throws RestApiException;
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index 51288e4..3b1f7f2 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -191,7 +192,7 @@
     try {
       ChangeResource rsrc =
           new ChangeResource(changeFactory.controlFor(change, user));
-      PostReviewers.Input input = new PostReviewers.Input();
+      AddReviewerInput input = new AddReviewerInput();
       input.reviewer = projectOwners;
       reviewersProvider.get().apply(rsrc, input);
     } catch (Exception e) {
@@ -209,7 +210,7 @@
       try {
         ChangeResource rsrc =
             new ChangeResource(changeFactory.controlFor(change, user));
-        PostReviewers.Input input = new PostReviewers.Input();
+        AddReviewerInput input = new AddReviewerInput();
         input.reviewer = r.getGroup().getUUID().get();
         reviewersProvider.get().apply(rsrc, input);
       } catch (Exception e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index e3149da..bb231e6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.AbandonInput;
+import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.ChangeApi;
 import com.google.gerrit.extensions.api.changes.Changes;
 import com.google.gerrit.extensions.api.changes.RestoreInput;
@@ -25,6 +26,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.change.Abandon;
 import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PostReviewers;
 import com.google.gerrit.server.change.Restore;
 import com.google.gerrit.server.change.Revert;
 import com.google.gerrit.server.change.Revisions;
@@ -47,6 +49,7 @@
   private final Provider<Abandon> abandon;
   private final Provider<Revert> revert;
   private final Provider<Restore> restore;
+  private final Provider<PostReviewers> postReviewers;
 
   @Inject
   ChangeApiImpl(Changes changeApi,
@@ -55,6 +58,7 @@
       Provider<Abandon> abandon,
       Provider<Revert> revert,
       Provider<Restore> restore,
+      Provider<PostReviewers> postReviewers,
       @Assisted ChangeResource change) {
     this.changeApi = changeApi;
     this.revert = revert;
@@ -62,6 +66,7 @@
     this.revisionApi = revisionApi;
     this.abandon = abandon;
     this.restore = restore;
+    this.postReviewers = postReviewers;
     this.change = change;
   }
 
@@ -131,4 +136,13 @@
       throw new RestApiException("Cannot revert change", e);
     }
   }
+
+  @Override
+  public void addReviewer(AddReviewerInput in) throws RestApiException {
+    try {
+      postReviewers.get().apply(change, in);
+    } catch (OrmException | EmailException | IOException e) {
+      throw new RestApiException("Cannot add change reviewer", e);
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index fe3e00c..306e5a8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -25,9 +25,9 @@
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.account.AccountInfo;
 import com.google.gerrit.server.account.AccountsCollection;
 import com.google.gerrit.server.account.GroupMembers;
-import com.google.gerrit.server.change.PostReviewers.Input;
 import com.google.gerrit.server.change.ReviewerJson.PostResult;
 import com.google.gerrit.server.change.ReviewerJson.ReviewerInfo;
 import com.google.gerrit.server.config.GerritServerConfig;
@@ -67,23 +66,13 @@
 import java.util.List;
 import java.util.Set;
 
-public class PostReviewers implements RestModifyView<ChangeResource, Input> {
+public class PostReviewers implements RestModifyView<ChangeResource, AddReviewerInput> {
   private static final Logger log = LoggerFactory
       .getLogger(PostReviewers.class);
 
   public static final int DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK = 10;
   public static final int DEFAULT_MAX_REVIEWERS = 20;
 
-  public static class Input {
-    @DefaultInput
-    public String reviewer;
-    Boolean confirmed;
-
-    boolean confirmed() {
-      return Objects.firstNonNull(confirmed, false);
-    }
-  }
-
   private final AccountsCollection accounts;
   private final ReviewerResource.Factory reviewerFactory;
   private final AddReviewerSender.Factory addReviewerSenderFactory;
@@ -131,7 +120,7 @@
   }
 
   @Override
-  public PostResult apply(ChangeResource rsrc, Input input)
+  public PostResult apply(ChangeResource rsrc, AddReviewerInput input)
       throws AuthException, BadRequestException, UnprocessableEntityException,
       OrmException, EmailException, IOException {
     if (input.reviewer == null) {
@@ -159,7 +148,7 @@
     return result;
   }
 
-  private PostResult putGroup(ChangeResource rsrc, Input input)
+  private PostResult putGroup(ChangeResource rsrc, AddReviewerInput input)
       throws BadRequestException,
       UnprocessableEntityException, OrmException, EmailException, IOException {
     GroupDescription.Basic group = groupsCollection.get().parseInternal(input.reviewer);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index ce015b6..9a20133 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.sshd.commands;
 
+import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -137,7 +138,7 @@
     //
     PostReviewers post = postReviewersProvider.get();
     for (String reviewer : toAdd) {
-      PostReviewers.Input input = new PostReviewers.Input();
+      AddReviewerInput input = new AddReviewerInput();
       input.reviewer = reviewer;
       String error;
       try {