Merge branch 'stable-2.12' into stable-2.13

* stable-2.12:
  Update reviewers on DraftPublishedEvent

Change-Id: Ic25c5e69c4d019554284c562bdda90833a366091
diff --git a/lib/gerrit/BUCK b/lib/gerrit/BUCK
index 04e837e..88a9696 100644
--- a/lib/gerrit/BUCK
+++ b/lib/gerrit/BUCK
@@ -1,12 +1,12 @@
 include_defs('//bucklets/maven_jar.bucklet')
 
-VER = '2.12.2'
+VER = '2.13'
 REPO = MAVEN_CENTRAL
 
 maven_jar(
   name = 'plugin-api',
   id = 'com.google.gerrit:gerrit-plugin-api:' + VER,
-  sha1 = '621012e67c64774d9743d7c7c8b0cae0998fae0d',
+  sha1 = 'e25d55b8f41627c4ae6b9d2069ec398638b219a3',
   license = 'Apache2.0',
   attach_source = False,
   repository = REPO,
@@ -15,7 +15,7 @@
 maven_jar(
   name = 'gwtui-api',
   id = 'com.google.gerrit:gerrit-plugin-gwtui:' + VER,
-  sha1 = '272652e0a7a76e8eba27891823701ecd048305e5',
+  sha1 = '0890414f42fc1fd0fef0400a479836f558727234',
   license = 'Apache2.0',
   attach_source = False,
   repository = REPO,
diff --git a/pom.xml b/pom.xml
index b4b379d..47da116 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
   <groupId>com.googlesource.gerrit.plugins.reviewers</groupId>
   <artifactId>reviewers</artifactId>
   <packaging>jar</packaging>
-  <version>2.12.2</version>
+  <version>2.13</version>
   <name>reviewers</name>
 
   <properties>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java
index 2281997..3811d01 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
@@ -33,6 +32,7 @@
 import com.google.gerrit.server.account.AccountByEmailCache;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.GroupMembers;
+import com.google.gerrit.server.data.ChangeAttribute;
 import com.google.gerrit.server.events.DraftPublishedEvent;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.PatchSetCreatedEvent;
@@ -51,6 +51,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
 
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -61,6 +62,7 @@
 import java.util.List;
 import java.util.Set;
 
+@Singleton
 class ChangeEventListener implements EventListener {
   private static final Logger log = LoggerFactory
       .getLogger(ChangeEventListener.class);
@@ -79,7 +81,6 @@
   private final ReviewersConfig.Factory configFactory;
   private final Provider<CurrentUser> user;
   private final ChangeQueryBuilder queryBuilder;
-  private ReviewDb db;
 
   @Inject
   ChangeEventListener(
@@ -117,19 +118,19 @@
   public void onEvent(Event event) {
     if (event instanceof PatchSetCreatedEvent) {
       PatchSetCreatedEvent e = (PatchSetCreatedEvent) event;
-      onEvent(new Project.NameKey(e.change.project),
-          Integer.parseInt(e.change.number),
-          Integer.parseInt(e.patchSet.number), e.uploader.email);
+      ChangeAttribute c = e.change.get();
+      onEvent(new Project.NameKey(c.project),
+          Integer.parseInt(e.change.get().number), e.uploader.get().email);
     } else if (event instanceof DraftPublishedEvent) {
       DraftPublishedEvent e = (DraftPublishedEvent) event;
-      onEvent(new Project.NameKey(e.change.project),
-          Integer.parseInt(e.change.number),
-          Integer.parseInt(e.patchSet.number), e.uploader.email);
+      ChangeAttribute c = e.change.get();
+      onEvent(new Project.NameKey(c.project),
+          Integer.parseInt(e.change.get().number), e.uploader.get().email);
     }
   }
 
   private void onEvent(Project.NameKey projectName, int changeNumber,
-      int patchSetNumber, String email) {
+      String email) {
 
     // TODO(davido): we have to cache per project configuration
     ReviewersConfig config = configFactory.create(projectName);
@@ -142,31 +143,20 @@
     try (Repository git = repoManager.openRepository(projectName);
         RevWalk rw = new RevWalk(git);
         ReviewDb reviewDb = schemaFactory.open()) {
-      Change.Id changeId = new Change.Id(changeNumber);
-      PatchSet.Id psId =
-          new PatchSet.Id(changeId, patchSetNumber);
-      PatchSet ps = reviewDb.patchSets().get(psId);
-      if (ps == null) {
-        log.warn("Patch set " + psId.get() + " not found.");
-        return;
-      }
-
-      final Change change = reviewDb.changes().get(psId.getParentKey());
-      if (change == null) {
-        log.warn("Change " + changeId.get() + " not found.");
-        return;
-      }
-
-      Set<String> reviewers = findReviewers(sections, reviewDb, change);
+      ChangeData changeData = changeDataFactory.create(
+          reviewDb, projectName, new Change.Id(changeNumber));
+      Set<String> reviewers = findReviewers(sections, changeData);
       if (reviewers.isEmpty()) {
         return;
       }
 
-      final Runnable task =
-          reviewersFactory.create(change,
-              toAccounts(reviewers, projectName, email));
+      final Change change = changeData.change();
+      final Runnable task = reviewersFactory.create(change,
+          toAccounts(reviewDb, reviewers, projectName, email));
 
       workQueue.getDefaultQueue().submit(new Runnable() {
+        ReviewDb db = null;
+
         @Override
         public void run() {
           RequestContext old = tl.setContext(new RequestContext() {
@@ -209,11 +199,11 @@
     }
   }
 
-  private Set<String> findReviewers(
-      List<ReviewerFilterSection> sections, final ReviewDb reviewDb,
-      final Change change) throws OrmException, QueryParseException {
+  private Set<String> findReviewers(List<ReviewerFilterSection> sections,
+      ChangeData changeData) throws OrmException, QueryParseException {
     ImmutableSet.Builder<String> reviewers = ImmutableSet.builder();
-    List<ReviewerFilterSection> found = findReviewerSections(sections, reviewDb, change);
+    List<ReviewerFilterSection> found = findReviewerSections(sections,
+        changeData);
     for (ReviewerFilterSection s : found) {
       reviewers.addAll(s.getReviewers());
     }
@@ -221,10 +211,9 @@
   }
 
   private List<ReviewerFilterSection> findReviewerSections(
-      List<ReviewerFilterSection> sections, final ReviewDb reviewDb,
-      final Change change) throws OrmException, QueryParseException {
+      List<ReviewerFilterSection> sections, ChangeData changeData)
+          throws OrmException, QueryParseException {
     ImmutableList.Builder<ReviewerFilterSection> found = ImmutableList.builder();
-    ChangeData changeData = changeDataFactory.create(reviewDb, change);
     for (ReviewerFilterSection s : sections) {
       if (Strings.isNullOrEmpty(s.getFilter())
           || s.getFilter().equals("*")) {
@@ -244,16 +233,16 @@
     // TODO(davido): check that the potential review can see this change
     // by adding AND is_visible() predicate? Or is it OK to assume
     // that reviewers always can see it?
-    return filterPredicate.match(changeData);
+    return filterPredicate.asMatchable().match(changeData);
   }
 
-  private Set<Account> toAccounts(Set<String> in, Project.NameKey p,
-      String uploaderEMail) {
+  private Set<Account> toAccounts(ReviewDb reviewDb, Set<String> in,
+      Project.NameKey p, String uploaderEMail) {
     Set<Account> reviewers = Sets.newHashSetWithExpectedSize(in.size());
     GroupMembers groupMembers = null;
     for (String r : in) {
       try {
-        Account account = accountResolver.find(r);
+        Account account = accountResolver.find(reviewDb, r);
         if (account != null) {
           reviewers.add(account);
           continue;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java
index c5ce186..d721227 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java
@@ -15,6 +15,8 @@
 package com.googlesource.gerrit.plugins.reviewers;
 
 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.api.changes.ChangeApi;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -25,6 +27,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.Set;
 
 class DefaultReviewers implements Runnable {
@@ -62,10 +65,19 @@
    */
   private void addReviewers(Set<Account> reviewers, Change change) {
     try {
-      ChangeApi cApi = gApi.changes().id(change.getId().get());
+      // TODO(davido): Switch back to using changes API again,
+      // when it supports batch mode for adding reviewers
+      ReviewInput in = new ReviewInput();
+      in.reviewers = new ArrayList<>(reviewers.size());
       for (Account account : reviewers) {
-        cApi.addReviewer(account.getId().toString());
+        AddReviewerInput addReviewerInput = new AddReviewerInput();
+        addReviewerInput.reviewer = account.getId().toString();
+        in.reviewers.add(addReviewerInput);
       }
+      gApi.changes()
+          .id(change.getId().get())
+          .current()
+          .review(in);
     } catch (RestApiException e) {
       log.error("Couldn't add reviewers to the change", e);
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
index e7dc004..b83ea10 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
@@ -14,8 +14,6 @@
 
 package com.googlesource.gerrit.plugins.reviewers;
 
-import com.google.common.base.Objects;
-import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -23,11 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.group.GroupsCollection;
@@ -42,7 +37,6 @@
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,28 +58,25 @@
   private final ReviewersConfig.Factory configFactory;
   private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
   private final ProjectCache projectCache;
-  private final Provider<CurrentUser> currentUser;
-  private final ChangeHooks hooks;
   private final AccountResolver accountResolver;
   private final Provider<GroupsCollection> groupsCollection;
+  private final Provider<ReviewDb> reviewDbProvider;
 
   @Inject
   PutReviewers(@PluginName String pluginName,
       ReviewersConfig.Factory configFactory,
       Provider<MetaDataUpdate.User> metaDataUpdateFactory,
       ProjectCache projectCache,
-      ChangeHooks hooks,
-      Provider<CurrentUser> currentUser,
       AccountResolver accountResolver,
-      Provider<GroupsCollection> groupsCollection) {
+      Provider<GroupsCollection> groupsCollection,
+      Provider<ReviewDb> reviewDbProvider) {
     this.pluginName = pluginName;
     this.configFactory = configFactory;
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.projectCache = projectCache;
-    this.hooks = hooks;
-    this.currentUser = currentUser;
     this.accountResolver = accountResolver;
     this.groupsCollection = groupsCollection;
+    this.reviewDbProvider = reviewDbProvider;
   }
 
   @Override
@@ -97,72 +88,58 @@
       throw new ResourceNotFoundException(
           "Project" + projectName.get() + " not found");
     }
-    MetaDataUpdate md;
-    try {
-      md = metaDataUpdateFactory.get().create(projectName);
-    } catch (RepositoryNotFoundException notFound) {
-      throw new ResourceNotFoundException(
-          "Project" + projectName.get() + " not found");
-    } catch (IOException e) {
-      throw new ResourceNotFoundException(
-          "Project" + projectName.get() + " not found", e);
-    }
-    if (input.action == Action.ADD) {
-      validateReviewer(input.reviewer);
-    }
-    try {
-      StringBuilder message = new StringBuilder(pluginName)
-          .append(" plugin: ");
-      cfg.load(md);
+
+    try (MetaDataUpdate md = metaDataUpdateFactory.get().create(projectName)) {
       if (input.action == Action.ADD) {
-        message.append("Add reviewer ")
-          .append(input.reviewer)
-          .append(" to filter ")
-          .append(input.filter);
-        cfg.addReviewer(input.filter, input.reviewer);
-      } else {
-        message.append("Remove reviewer ")
-          .append(input.reviewer)
-          .append(" from filter ")
-          .append(input.filter);
-        cfg.removeReviewer(input.filter, input.reviewer);
+        validateReviewer(input.reviewer);
       }
-      message.append("\n");
-      md.setMessage(message.toString());
       try {
-        ObjectId baseRev = cfg.getRevision();
-        ObjectId commitRev = cfg.commit(md);
-        // Only fire hook if project was actually changed.
-        if (!Objects.equal(baseRev, commitRev)) {
-          IdentifiedUser user = (IdentifiedUser) currentUser.get();
-          hooks.doRefUpdatedHook(
-            new Branch.NameKey(projectName, RefNames.REFS_CONFIG),
-            baseRev, commitRev, user.getAccount());
-        }
-        projectCache.evict(projectName);
-      } catch (IOException e) {
-        if (e.getCause() instanceof ConfigInvalidException) {
-          throw new ResourceConflictException("Cannot update " + projectName
-              + ": " + e.getCause().getMessage());
+        StringBuilder message = new StringBuilder(pluginName)
+            .append(" plugin: ");
+        cfg.load(md);
+        if (input.action == Action.ADD) {
+          message.append("Add reviewer ")
+            .append(input.reviewer)
+            .append(" to filter ")
+            .append(input.filter);
+          cfg.addReviewer(input.filter, input.reviewer);
         } else {
+          message.append("Remove reviewer ")
+            .append(input.reviewer)
+            .append(" from filter ")
+            .append(input.filter);
+          cfg.removeReviewer(input.filter, input.reviewer);
+        }
+        message.append("\n");
+        md.setMessage(message.toString());
+        try {
+          cfg.commit(md);
+          projectCache.evict(projectName);
+        } catch (IOException e) {
+          if (e.getCause() instanceof ConfigInvalidException) {
+            throw new ResourceConflictException("Cannot update " + projectName
+                + ": " + e.getCause().getMessage());
+          }
           throw new ResourceConflictException("Cannot update " + projectName);
         }
+      } catch (ConfigInvalidException err) {
+        throw new ResourceConflictException("Cannot read " + pluginName
+            + " configurations for project " + projectName, err);
+      } catch (IOException err) {
+        throw new ResourceConflictException("Cannot update " + pluginName
+            + " configurations for project " + projectName, err);
       }
-    } catch (ConfigInvalidException err) {
-      throw new ResourceConflictException("Cannot read " + pluginName
-          + " configurations for project " + projectName, err);
+    } catch (RepositoryNotFoundException err) {
+      throw new ResourceNotFoundException(projectName.get());
     } catch (IOException err) {
-      throw new ResourceConflictException("Cannot update " + pluginName
-          + " configurations for project " + projectName, err);
-    } finally {
-      md.close();
+      throw new ResourceNotFoundException(projectName.get(), err);
     }
     return cfg.getReviewerFilterSections();
   }
 
   private void validateReviewer(String reviewer) throws RestApiException {
     try {
-      Account account = accountResolver.find(reviewer);
+      Account account = accountResolver.find(reviewDbProvider.get(), reviewer);
       if (account == null) {
         try {
           groupsCollection.get().parse(reviewer);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersTopMenu.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersTopMenu.java
index fed0531..b905858 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersTopMenu.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersTopMenu.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.client.MenuItem;
 import com.google.gerrit.extensions.webui.TopMenu;
 import com.google.inject.Inject;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java
index 0e7e67c..c251941 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java
@@ -59,7 +59,7 @@
       @Override
       public boolean isVisibleTo(Account.Id account) throws OrmException {
         IdentifiedUser who =
-            identifiedUserFactory.create(dbProvider, account);
+            identifiedUserFactory.create(account);
         return rsrc.getControl().forUser(who).isVisible();
       }
     };
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ChangeReviewersInput.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ChangeReviewersInput.java
index 392fa75..d5f955e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ChangeReviewersInput.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ChangeReviewersInput.java
@@ -28,7 +28,7 @@
     setActionRaw(a.name());
   }
 
-  private final native void setActionRaw(String a)
+  final native void setActionRaw(String a)
   /*-{ if(a)this.action=a; }-*/;
 
   final native void setFilter(String f)
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 96d54b5..b678ff5 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -1,7 +1,7 @@
 A plugin that allows adding default reviewers to a change.
 
 The configuration for adding reviewers to submitted changes can be
-[configured per project](config.html).
+[configured per project](config.md).
 
 SEE ALSO
 --------
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index c4f6732..dc33468 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -31,6 +31,12 @@
   cd reviewers && ln -s bucklets/buckversion .buckversion
 ```
 
+Add link to the .watchmanconfig file:
+
+```
+  cd reviewers && ln -s bucklets/watchmanconfig .watchmanconfig
+```
+
 To build the plugin, issue the following command: