Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Upgrade bazlets to latest stable-2.16
  Add reviewers from OWNERS all at once
  Implement visibility using permission backend
  Do not assign reviewers that cannot see the change
  Fix NPE when target branch doesn't exist

Change-Id: Ie7d6439bf2723696cf2920fbbe8a2ca7049a8e2d
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java
index 4a16316..3634cbe 100644
--- a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java
@@ -23,6 +23,14 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Account.Id;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.inject.Inject;
@@ -38,11 +46,22 @@
 
   private final OneOffRequestContext requestContext;
   private final GerritApi gApi;
+  private final IdentifiedUser.GenericFactory userFactory;
+  private final ChangeData.Factory changeDataFactory;
+  private final PermissionBackend permissionBackend;
 
   @Inject
-  public ReviewerManager(OneOffRequestContext requestContext, GerritApi gApi) {
+  public ReviewerManager(
+      OneOffRequestContext requestContext,
+      GerritApi gApi,
+      IdentifiedUser.GenericFactory userFactory,
+      ChangeData.Factory changeDataFactory,
+      PermissionBackend permissionBackend) {
     this.requestContext = requestContext;
     this.gApi = gApi;
+    this.userFactory = userFactory;
+    this.changeDataFactory = changeDataFactory;
+    this.permissionBackend = permissionBackend;
   }
 
   public void addReviewers(ChangeApi cApi, Collection<Account.Id> reviewers)
@@ -56,9 +75,16 @@
         ReviewInput in = new ReviewInput();
         in.reviewers = new ArrayList<>(reviewers.size());
         for (Account.Id account : reviewers) {
-          AddReviewerInput addReviewerInput = new AddReviewerInput();
-          addReviewerInput.reviewer = account.toString();
-          in.reviewers.add(addReviewerInput);
+          if (isVisibleTo(ctx.getReviewDbProvider().get(), changeInfo, account)) {
+            AddReviewerInput addReviewerInput = new AddReviewerInput();
+            addReviewerInput.reviewer = account.toString();
+            in.reviewers.add(addReviewerInput);
+          } else {
+            log.warn(
+                "Not adding account {} as reviewer to change {} because the associated ref is not visible",
+                account,
+                changeInfo._number);
+          }
         }
         gApi.changes().id(changeInfo.id).current().review(in);
       }
@@ -67,4 +93,14 @@
       throw new ReviewerManagerException(e);
     }
   }
+
+  private boolean isVisibleTo(ReviewDb reviewDb, ChangeInfo changeInfo, Id account) {
+    ChangeData changeData =
+        changeDataFactory.create(
+            reviewDb, new Project.NameKey(changeInfo.project), new Change.Id(changeInfo._number));
+    return permissionBackend
+        .user(userFactory.create(account))
+        .change(changeData)
+        .testOrFalse(ChangePermission.READ);
+  }
 }
diff --git a/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java b/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java
index 9668af8..c1a7368 100644
--- a/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java
+++ b/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java
@@ -34,9 +34,14 @@
 
   public static Optional<byte[]> getBlobAsBytes(Repository repository, String revision, String path)
       throws IOException {
+    ObjectId objectId = repository.resolve(revision);
+    if (objectId == null) {
+      return Optional.empty();
+    }
+
     try (final TreeWalk w =
         TreeWalk.forPath(
-            repository, path, parseCommit(repository, repository.resolve(revision)).getTree())) {
+            repository, path, parseCommit(repository, objectId).getTree())) {
 
       return Optional.ofNullable(w)
           .filter(walk -> (walk.getRawMode(0) & TYPE_MASK) == TYPE_FILE)