Merge branch 'stable-2.14' into stable-2.15

* stable-2.14:
  Upgrade bazlets to latest stable-2.14 to build with 2.14.21 API

Change-Id: Id3263757b949be043d64349ef0a252bd86612e97
diff --git a/BUILD b/BUILD
index 8c5d04c..2699f2b 100644
--- a/BUILD
+++ b/BUILD
@@ -7,9 +7,31 @@
     "gerrit_plugin",
 )
 
+SRC = "src/main/java/com/googlesource/gerrit/plugins/reviewers/"
+
+BACKEND_SRCS = glob([
+    SRC + "common/*.java",
+    SRC + "server/*.java",
+])
+
+gerrit_plugin(
+    name = "reviewers-backend",
+    srcs = BACKEND_SRCS,
+    dir_name = "reviewers",
+    manifest_entries = [
+        # Different jar name, but use same plugin name in manifest so REST API is compatible.
+        "Gerrit-PluginName: reviewers",
+        "Gerrit-Module: com.googlesource.gerrit.plugins.reviewers.server.BackendModule",
+    ],
+    resources = glob(["src/main/resources/**/*"]),
+)
+
 gerrit_plugin(
     name = "reviewers",
-    srcs = glob(["src/main/java/**/*.java"]),
+    srcs = BACKEND_SRCS + glob([
+        SRC + "*.java",
+        SRC + "client/*.java",
+    ]),
     gwt_module = "com.googlesource.gerrit.plugins.reviewers.ReviewersForm",
     manifest_entries = [
         "Gerrit-PluginName: reviewers",
diff --git a/WORKSPACE b/WORKSPACE
index 7a13562..f36ac9a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "78c35a7eb33ee5ea0980923e246c7dba37347193",
+    commit = "f53f51fb660552d0581aa0ba52c3836ed63d56a3",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ClientModule.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ClientModule.java
new file mode 100644
index 0000000..4aac38f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ClientModule.java
@@ -0,0 +1,29 @@
+// 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.googlesource.gerrit.plugins.reviewers;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.GwtPlugin;
+import com.google.gerrit.extensions.webui.TopMenu;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
+import com.google.inject.AbstractModule;
+
+public class ClientModule extends AbstractModule {
+  @Override
+  protected void configure() {
+    DynamicSet.bind(binder(), TopMenu.class).to(ReviewersTopMenu.class);
+    DynamicSet.bind(binder(), WebUiPlugin.class).toInstance(new GwtPlugin("reviewers"));
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
index ec39ffc..55b663c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2018 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.
@@ -14,73 +14,22 @@
 
 package com.googlesource.gerrit.plugins.reviewers;
 
-import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
-
-import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.extensions.events.DraftPublishedListener;
-import com.google.gerrit.extensions.events.RevisionCreatedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.extensions.webui.GwtPlugin;
-import com.google.gerrit.extensions.webui.TopMenu;
-import com.google.gerrit.extensions.webui.WebUiPlugin;
-import com.google.gerrit.server.change.ReviewerSuggestion;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.reviewers.server.BackendModule;
+import com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig;
 
-public class Module extends FactoryModule {
-  private final boolean enableUI;
-  private final boolean enableREST;
-  private final boolean suggestOnly;
+public class Module extends AbstractModule {
+  private final ReviewersConfig cfg;
 
   @Inject
   public Module(ReviewersConfig cfg) {
-    this.enableREST = cfg.enableREST();
-    this.enableUI = cfg.enableUI();
-    this.suggestOnly = cfg.suggestOnly();
-  }
-
-  public Module(boolean enableUI, boolean enableREST, boolean suggestOnly) {
-    this.enableUI = enableUI;
-    this.enableREST = enableREST;
-    this.suggestOnly = suggestOnly;
+    this.cfg = cfg;
   }
 
   @Override
   protected void configure() {
-    if (enableUI) {
-      DynamicSet.bind(binder(), TopMenu.class).to(ReviewersTopMenu.class);
-      DynamicSet.bind(binder(), WebUiPlugin.class).toInstance(new GwtPlugin("reviewers"));
-    }
-
-    if (suggestOnly) {
-      install(
-          new AbstractModule() {
-            @Override
-            protected void configure() {
-              bind(ReviewerSuggestion.class)
-                  .annotatedWith(Exports.named("reviewer-suggest"))
-                  .to(Reviewers.class);
-            }
-          });
-    } else {
-      DynamicSet.bind(binder(), RevisionCreatedListener.class).to(Reviewers.class);
-      DynamicSet.bind(binder(), DraftPublishedListener.class).to(Reviewers.class);
-    }
-
-    factory(AddReviewersByConfiguration.Factory.class);
-
-    if (enableREST) {
-      install(
-          new RestApiModule() {
-            @Override
-            protected void configure() {
-              get(PROJECT_KIND, "reviewers").to(GetReviewers.class);
-              put(PROJECT_KIND, "reviewers").to(PutReviewers.class);
-              get(PROJECT_KIND, "suggest_reviewers").to(SuggestProjectReviewers.class);
-            }
-          });
-    }
+    install(new BackendModule(cfg.enableREST(), cfg.suggestOnly()));
+    install(new ClientModule());
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/AccountCapabilities.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/AccountCapabilities.java
new file mode 100644
index 0000000..397ffcb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/AccountCapabilities.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2018 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.reviewers.client;
+
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public class AccountCapabilities extends JavaScriptObject {
+  static String MODIFY_REVIEWERS_CONFIG = "reviewers-modifyReviewersConfig";
+
+  public static void queryPluginCapability(AsyncCallback<AccountCapabilities> cb) {
+    new RestApi("/accounts/self/capabilities").addParameter("q", MODIFY_REVIEWERS_CONFIG).get(cb);
+  }
+
+  protected AccountCapabilities() {}
+
+  public final native boolean canPerform(String name) /*-{ return this[name] ? true : false; }-*/;
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ReviewersScreen.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ReviewersScreen.java
index ba601ae..47bedd7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ReviewersScreen.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/client/ReviewersScreen.java
@@ -14,6 +14,8 @@
 
 package com.googlesource.gerrit.plugins.reviewers.client;
 
+import static com.googlesource.gerrit.plugins.reviewers.client.AccountCapabilities.MODIFY_REVIEWERS_CONFIG;
+
 import com.google.gerrit.client.rpc.NativeMap;
 import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.plugin.client.rpc.RestApi;
@@ -48,10 +50,11 @@
   }
 
   private boolean isOwner;
+  private boolean hasModifyReviewersConfigCapability;
   private String projectName;
   private Set<ReviewerEntry> rEntries;
 
-  ReviewersScreen(final String projectName) {
+  ReviewersScreen(String projectName) {
     setStyleName("reviewers-panel");
     this.projectName = projectName;
     this.rEntries = new HashSet<>();
@@ -64,7 +67,24 @@
               @Override
               public void onSuccess(NativeMap<ProjectAccessInfo> result) {
                 isOwner = result.get(projectName).isOwner();
-                display();
+                if (isOwner) {
+                  display();
+                } else {
+                  // TODO(davido): Find a way to run above and below requests in parallel
+                  AccountCapabilities.queryPluginCapability(
+                      new AsyncCallback<AccountCapabilities>() {
+
+                        @Override
+                        public void onSuccess(AccountCapabilities result) {
+                          hasModifyReviewersConfigCapability =
+                              result.canPerform(MODIFY_REVIEWERS_CONFIG);
+                          display();
+                        }
+
+                        @Override
+                        public void onFailure(Throwable caught) {}
+                      });
+                }
               }
 
               @Override
@@ -124,7 +144,7 @@
             doSave(Action.REMOVE, e);
           }
         });
-    removeButton.setVisible(isOwner);
+    removeButton.setVisible(isModifiable());
 
     HorizontalPanel p = new HorizontalPanel();
     p.add(l);
@@ -161,9 +181,9 @@
             reviewerBox.setText("");
           }
         });
-    filterBox.setEnabled(isOwner);
-    reviewerBox.setEnabled(isOwner);
-    addButton.setEnabled(isOwner);
+    filterBox.setEnabled(isModifiable());
+    reviewerBox.setEnabled(isModifiable());
+    addButton.setEnabled(isModifiable());
 
     Panel p = new VerticalPanel();
     p.setStyleName("reviewers-inputPanel");
@@ -172,6 +192,10 @@
     return p;
   }
 
+  boolean isModifiable() {
+    return isOwner || hasModifyReviewersConfigCapability;
+  }
+
   void doSave(Action action, ReviewerEntry entry) {
     ChangeReviewersInput in = ChangeReviewersInput.create();
     in.setAction(action);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Action.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/common/Action.java
similarity index 91%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/Action.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/common/Action.java
index 04a15fc..ebda5e1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Action.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/common/Action.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.common;
 
 public enum Action {
   ADD,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/AddReviewers.java
similarity index 98%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/AddReviewers.java
index afbb377..be04ea4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/AddReviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewersByConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/AddReviewersByConfiguration.java
similarity index 96%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewersByConfiguration.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/AddReviewersByConfiguration.java
index a03c035..1ef93f2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewersByConfiguration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/AddReviewersByConfiguration.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.common.ChangeInfo;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/BackendModule.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/BackendModule.java
new file mode 100644
index 0000000..fc98943
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/BackendModule.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2018 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.reviewers.server;
+
+import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
+import static com.googlesource.gerrit.plugins.reviewers.server.ModifyReviewersConfigCapability.MODIFY_REVIEWERS_CONFIG;
+
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.CapabilityDefinition;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.events.PrivateStateChangedListener;
+import com.google.gerrit.extensions.events.RevisionCreatedListener;
+import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.RestApiModule;
+import com.google.gerrit.server.change.ReviewerSuggestion;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+
+public class BackendModule extends FactoryModule {
+  private final boolean enableREST;
+  private final boolean suggestOnly;
+
+  @Inject
+  public BackendModule(ReviewersConfig cfg) {
+    this(cfg.enableREST(), cfg.suggestOnly());
+  }
+
+  public BackendModule(boolean enableREST, boolean suggestOnly) {
+    this.enableREST = enableREST;
+    this.suggestOnly = suggestOnly;
+  }
+
+  @Override
+  protected void configure() {
+    bind(CapabilityDefinition.class)
+        .annotatedWith(Exports.named(MODIFY_REVIEWERS_CONFIG))
+        .to(ModifyReviewersConfigCapability.class);
+
+    if (suggestOnly) {
+      install(
+          new AbstractModule() {
+            @Override
+            protected void configure() {
+              bind(ReviewerSuggestion.class)
+                  .annotatedWith(Exports.named("reviewer-suggest"))
+                  .to(Reviewers.class);
+            }
+          });
+    } else {
+      DynamicSet.bind(binder(), RevisionCreatedListener.class).to(Reviewers.class);
+      DynamicSet.bind(binder(), WorkInProgressStateChangedListener.class).to(Reviewers.class);
+      DynamicSet.bind(binder(), PrivateStateChangedListener.class).to(Reviewers.class);
+    }
+
+    factory(AddReviewersByConfiguration.Factory.class);
+
+    if (enableREST) {
+      install(
+          new RestApiModule() {
+            @Override
+            protected void configure() {
+              get(PROJECT_KIND, "reviewers").to(GetReviewers.class);
+              put(PROJECT_KIND, "reviewers").to(PutReviewers.class);
+              get(PROJECT_KIND, "suggest_reviewers").to(SuggestProjectReviewers.class);
+            }
+          });
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/GetReviewers.java
similarity index 95%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/GetReviewers.java
index 7ee4c83..d8ce0ca 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/GetReviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ModifyReviewersConfigCapability.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ModifyReviewersConfigCapability.java
new file mode 100644
index 0000000..b97e422
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ModifyReviewersConfigCapability.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2018 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.reviewers.server;
+
+import com.google.gerrit.extensions.config.CapabilityDefinition;
+
+public class ModifyReviewersConfigCapability extends CapabilityDefinition {
+  static final String MODIFY_REVIEWERS_CONFIG = "modifyReviewersConfig";
+
+  @Override
+  public String getDescription() {
+    return "Modify Reviewers Config";
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java
similarity index 83%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java
index 9c02c6d..b90b350 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java
@@ -12,9 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
+
+import static com.googlesource.gerrit.plugins.reviewers.server.ModifyReviewersConfigCapability.MODIFY_REVIEWERS_CONFIG;
 
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.api.access.PluginPermission;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -22,17 +26,18 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Project;
-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;
+import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.reviewers.PutReviewers.Input;
+import com.googlesource.gerrit.plugins.reviewers.common.Action;
+import com.googlesource.gerrit.plugins.reviewers.server.PutReviewers.Input;
 import java.io.IOException;
 import java.util.List;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -56,7 +61,7 @@
   private final ProjectCache projectCache;
   private final AccountResolver accountResolver;
   private final Provider<GroupsCollection> groupsCollection;
-  private final Provider<ReviewDb> reviewDbProvider;
+  private final PermissionBackend permissionBackend;
 
   @Inject
   PutReviewers(
@@ -66,14 +71,14 @@
       ProjectCache projectCache,
       AccountResolver accountResolver,
       Provider<GroupsCollection> groupsCollection,
-      Provider<ReviewDb> reviewDbProvider) {
+      PermissionBackend permissionBackend) {
     this.pluginName = pluginName;
     this.config = config;
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.projectCache = projectCache;
     this.accountResolver = accountResolver;
     this.groupsCollection = groupsCollection;
-    this.reviewDbProvider = reviewDbProvider;
+    this.permissionBackend = permissionBackend;
   }
 
   @Override
@@ -81,10 +86,16 @@
       throws RestApiException {
     Project.NameKey projectName = rsrc.getNameKey();
     ReviewersConfig.ForProject cfg = config.forProject(projectName);
-    if (!rsrc.getControl().isOwner() || cfg == null) {
+    if (cfg == null) {
       throw new ResourceNotFoundException("Project" + projectName.get() + " not found");
     }
 
+    PermissionBackend.WithUser userPermission = permissionBackend.user(rsrc.getUser());
+    if (!rsrc.getControl().isOwner()
+        && !userPermission.testOrFalse(new PluginPermission(pluginName, MODIFY_REVIEWERS_CONFIG))) {
+      throw new AuthException("not allowed to modify reviewers config");
+    }
+
     try (MetaDataUpdate md = metaDataUpdateFactory.get().create(projectName)) {
       if (input.action == Action.ADD) {
         validateReviewer(input.reviewer);
@@ -136,7 +147,7 @@
 
   private void validateReviewer(String reviewer) throws RestApiException {
     try {
-      Account account = accountResolver.find(reviewDbProvider.get(), reviewer);
+      Account account = accountResolver.find(reviewer);
       if (account == null) {
         try {
           groupsCollection.get().parse(reviewer);
@@ -144,7 +155,7 @@
           throw new ResourceNotFoundException("Account or group " + reviewer + " not found");
         }
       }
-    } catch (OrmException e) {
+    } catch (OrmException | IOException | ConfigInvalidException e) {
       log.error("Failed to resolve account " + reviewer);
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilterSection.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewerFilterSection.java
similarity index 95%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilterSection.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewerFilterSection.java
index 06e7387..7f0000a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilterSection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewerFilterSection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import java.util.Objects;
 import java.util.Set;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/Reviewers.java
similarity index 84%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/Reviewers.java
index e5d1fcb..d21c538 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/Reviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import static java.util.stream.Collectors.toSet;
 
@@ -21,25 +21,23 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.events.DraftPublishedListener;
+import com.google.gerrit.extensions.events.ChangeEvent;
+import com.google.gerrit.extensions.events.PrivateStateChangedListener;
 import com.google.gerrit.extensions.events.RevisionCreatedListener;
-import com.google.gerrit.extensions.events.RevisionEvent;
+import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
+import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Account;
 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.CurrentUser;
 import com.google.gerrit.server.change.ReviewerSuggestion;
 import com.google.gerrit.server.change.SuggestedReviewer;
 import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.query.QueryParseException;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -49,13 +47,16 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
-class Reviewers implements RevisionCreatedListener, DraftPublishedListener, ReviewerSuggestion {
+class Reviewers
+    implements RevisionCreatedListener,
+        PrivateStateChangedListener,
+        WorkInProgressStateChangedListener,
+        ReviewerSuggestion {
   private static final Logger log = LoggerFactory.getLogger(Reviewers.class);
 
   private final ReviewersResolver resolver;
   private final AddReviewersByConfiguration.Factory byConfigFactory;
   private final WorkQueue workQueue;
-  private final SchemaFactory<ReviewDb> schemaFactory;
   private final ReviewersConfig config;
   private final Provider<CurrentUser> user;
   private final ChangeQueryBuilder queryBuilder;
@@ -66,7 +67,6 @@
       ReviewersResolver resolver,
       AddReviewersByConfiguration.Factory byConfigFactory,
       WorkQueue workQueue,
-      SchemaFactory<ReviewDb> schemaFactory,
       ReviewersConfig config,
       Provider<CurrentUser> user,
       ChangeQueryBuilder queryBuilder,
@@ -74,7 +74,6 @@
     this.resolver = resolver;
     this.byConfigFactory = byConfigFactory;
     this.workQueue = workQueue;
-    this.schemaFactory = schemaFactory;
     this.config = config;
     this.user = user;
     this.queryBuilder = queryBuilder;
@@ -87,7 +86,12 @@
   }
 
   @Override
-  public void onDraftPublished(DraftPublishedListener.Event event) {
+  public void onWorkInProgressStateChanged(WorkInProgressStateChangedListener.Event event) {
+    onEvent(event);
+  }
+
+  @Override
+  public void onPrivateStateChanged(PrivateStateChangedListener.Event event) {
     onEvent(event);
   }
 
@@ -103,10 +107,10 @@
       return ImmutableSet.of();
     }
 
-    try (ReviewDb reviewDb = schemaFactory.open()) {
+    try {
       Set<String> reviewers = findReviewers(changeId.get(), sections);
       if (!reviewers.isEmpty()) {
-        return resolver.resolve(reviewDb, reviewers, projectName, changeId.get(), null).stream()
+        return resolver.resolve(reviewers, projectName, changeId.get(), null).stream()
             .map(a -> suggestedReviewer(a))
             .collect(toSet());
       }
@@ -128,10 +132,12 @@
     return config.forProject(projectName).getReviewerFilterSections();
   }
 
-  private void onEvent(RevisionEvent event) {
+  private void onEvent(ChangeEvent event) {
     ChangeInfo c = event.getChange();
-    if (config.ignoreDrafts() && c.status == ChangeStatus.DRAFT) {
-      log.debug("Ignoring draft change");
+    if (config.ignoreWip() && (c.workInProgress != null && c.workInProgress)) {
+      return;
+    }
+    if (config.ignorePrivate() && (c.isPrivate != null && c.isPrivate)) {
       return;
     }
     Project.NameKey projectName = new Project.NameKey(c.project);
@@ -144,14 +150,14 @@
 
     AccountInfo uploader = event.getWho();
     int changeNumber = c._number;
-    try (ReviewDb reviewDb = schemaFactory.open()) {
+    try {
       Set<String> reviewers = findReviewers(changeNumber, sections);
       if (reviewers.isEmpty()) {
         return;
       }
       final Runnable task =
           byConfigFactory.create(
-              c, resolver.resolve(reviewDb, reviewers, projectName, changeNumber, uploader));
+              c, resolver.resolve(reviewers, projectName, changeNumber, uploader));
 
       workQueue.getDefaultQueue().submit(task);
     } catch (QueryParseException e) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfig.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java
similarity index 89%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfig.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java
index 569373c..b3e2ed2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
@@ -36,34 +36,34 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
-class ReviewersConfig {
+public class ReviewersConfig {
   private static final Logger log = LoggerFactory.getLogger(ReviewersConfig.class);
 
   static final String FILENAME = "reviewers.config";
   static final String SECTION_FILTER = "filter";
   static final String KEY_REVIEWER = "reviewer";
-  private static final String KEY_IGNORE_DRAFTS = "ignoreDrafts";
   private static final String KEY_ENABLE_REST = "enableREST";
-  private static final String KEY_ENABLE_UI = "enableUI";
   private static final String KEY_SUGGEST_ONLY = "suggestOnly";
+  private static final String KEY_IGNORE_WIP = "ignoreWip";
+  private static final String KEY_IGNORE_PRIVATE = "ignorePrivate";
 
   private final PluginConfigFactory cfgFactory;
   private final String pluginName;
 
-  private final boolean enableUI;
   private final boolean enableREST;
   private final boolean suggestOnly;
-  private final boolean ignoreDrafts;
+  private final boolean ignoreWip;
+  private final boolean ignorePrivate;
 
   @Inject
   ReviewersConfig(PluginConfigFactory cfgFactory, @PluginName String pluginName) {
     this.cfgFactory = cfgFactory;
     this.pluginName = pluginName;
     Config cfg = cfgFactory.getGlobalPluginConfig(pluginName);
-    this.ignoreDrafts = cfg.getBoolean(pluginName, null, KEY_IGNORE_DRAFTS, false);
     this.enableREST = cfg.getBoolean(pluginName, null, KEY_ENABLE_REST, true);
-    this.enableUI = enableREST ? cfg.getBoolean(pluginName, null, KEY_ENABLE_UI, true) : false;
     this.suggestOnly = cfg.getBoolean(pluginName, null, KEY_SUGGEST_ONLY, false);
+    this.ignoreWip = cfg.getBoolean(pluginName, null, KEY_IGNORE_WIP, true);
+    this.ignorePrivate = cfg.getBoolean(pluginName, null, KEY_IGNORE_PRIVATE, true);
   }
 
   public ForProject forProject(Project.NameKey projectName) {
@@ -77,22 +77,22 @@
     return new ForProject(cfg);
   }
 
-  public boolean ignoreDrafts() {
-    return ignoreDrafts;
-  }
-
   public boolean enableREST() {
     return enableREST;
   }
 
-  public boolean enableUI() {
-    return enableUI;
-  }
-
   public boolean suggestOnly() {
     return suggestOnly;
   }
 
+  public boolean ignoreWip() {
+    return ignoreWip;
+  }
+
+  public boolean ignorePrivate() {
+    return ignorePrivate;
+  }
+
   static class ForProject extends VersionedMetaData {
     private Config cfg;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersResolver.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java
similarity index 90%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersResolver.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java
index 0a05de2..f7fe4f3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersResolver.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import static java.util.stream.Collectors.toSet;
 
@@ -24,7 +24,6 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
 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.account.AccountResolver;
 import com.google.gerrit.server.account.GroupMembers;
@@ -36,6 +35,7 @@
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -65,7 +65,6 @@
    * Resolve a set of account names to {@link com.google.gerrit.reviewdb.client.Account.Id}s. Group
    * names are resolved to their account members.
    *
-   * @param reviewDb DB
    * @param names the set of account names to convert
    * @param project the project name
    * @param changeNumber the change Id
@@ -74,7 +73,6 @@
    */
   @VisibleForTesting
   Set<Account.Id> resolve(
-      ReviewDb reviewDb,
       Set<String> names,
       Project.NameKey project,
       int changeNumber,
@@ -82,12 +80,12 @@
     Set<Account.Id> reviewers = Sets.newHashSetWithExpectedSize(names.size());
     GroupMembers groupMembers = null;
     for (String name : names) {
-      if (resolveAccount(reviewDb, project, changeNumber, uploader, reviewers, name)) {
+      if (resolveAccount(project, changeNumber, uploader, reviewers, name)) {
         continue;
       }
 
       if (groupMembers == null && uploader != null) {
-        groupMembers = createGroupMembers(reviewDb, project, changeNumber, uploader, name);
+        groupMembers = createGroupMembers(project, changeNumber, uploader, name);
       }
 
       if (groupMembers != null) {
@@ -105,14 +103,13 @@
   }
 
   private boolean resolveAccount(
-      ReviewDb reviewDb,
       Project.NameKey project,
       int changeNumber,
       AccountInfo uploader,
       Set<Account.Id> reviewers,
       String accountName) {
     try {
-      Account account = accountResolver.find(reviewDb, accountName);
+      Account account = accountResolver.find(accountName);
       if (account != null) {
         if (account.isActive()) {
           if (uploader == null || uploader._accountId != account.getId().get()) {
@@ -126,7 +123,7 @@
             project,
             accountName);
       }
-    } catch (OrmException e) {
+    } catch (OrmException | IOException | ConfigInvalidException e) {
       // If the account doesn't exist, find() will return null.  We only
       // get here if something went wrong accessing the database
       log.error(
@@ -171,20 +168,16 @@
   }
 
   private GroupMembers createGroupMembers(
-      ReviewDb reviewDb,
-      Project.NameKey project,
-      int changeNumber,
-      AccountInfo uploader,
-      String group) {
+      Project.NameKey project, int changeNumber, AccountInfo uploader, String group) {
     // email is not unique to one account, try to locate the account using
     // "Full name <email>" to increase chance of finding only one.
     String uploaderNameEmail = String.format("%s <%s>", uploader.name, uploader.email);
     try {
-      Account uploaderAccount = accountResolver.findByNameOrEmail(reviewDb, uploaderNameEmail);
+      Account uploaderAccount = accountResolver.findByNameOrEmail(uploaderNameEmail);
       if (uploaderAccount != null) {
         return groupMembersFactory.create(identifiedUserFactory.create(uploaderAccount.getId()));
       }
-    } catch (OrmException e) {
+    } catch (OrmException | IOException e) {
       log.warn(
           "For the change {} of project {}: failed to list accounts for group {}, cannot retrieve uploader account {}.",
           changeNumber,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java
similarity index 71%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java
index f71c591..b8b076e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/SuggestProjectReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
+import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.RestReadView;
@@ -22,41 +23,50 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.ReviewersUtil;
 import com.google.gerrit.server.ReviewersUtil.VisibilityControl;
-import com.google.gerrit.server.account.AccountVisibility;
 import com.google.gerrit.server.change.SuggestReviewers;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.List;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
 
 public class SuggestProjectReviewers extends SuggestReviewers
     implements RestReadView<ProjectResource> {
+  private final PermissionBackend permissionBackend;
+
   @Inject
   SuggestProjectReviewers(
       AccountVisibility av,
       IdentifiedUser.GenericFactory identifiedUserFactory,
       Provider<ReviewDb> dbProvider,
       @GerritServerConfig Config cfg,
-      ReviewersUtil reviewersUtil) {
+      ReviewersUtil reviewersUtil,
+      PermissionBackend permissionBackend) {
     super(av, identifiedUserFactory, dbProvider, cfg, reviewersUtil);
+    this.permissionBackend = permissionBackend;
   }
 
   @Override
   public List<SuggestedReviewerInfo> apply(ProjectResource rsrc)
-      throws BadRequestException, OrmException, IOException {
-    return reviewersUtil.suggestReviewers(null, this, rsrc.getControl(), getVisibility(rsrc), true);
+      throws BadRequestException, OrmException, IOException, ConfigInvalidException {
+    return reviewersUtil.suggestReviewers(
+        null, this, rsrc.getProjectState(), getVisibility(rsrc), true);
   }
 
   private VisibilityControl getVisibility(final ProjectResource rsrc) {
     return new VisibilityControl() {
       @Override
       public boolean isVisibleTo(Account.Id account) throws OrmException {
-        IdentifiedUser who = identifiedUserFactory.create(account);
-        return rsrc.getControl().forUser(who).isVisible();
+        return permissionBackend
+            .user(identifiedUserFactory.create(account))
+            .project(rsrc.getNameKey())
+            .testOrFalse(ProjectPermission.ACCESS);
       }
     };
   }
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index 8f842c4..fcbf9ae 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -57,5 +57,13 @@
   ./tools/eclipse/project.py
 ```
 
+### Backend-only
+
+There are two separate plugin targets, one containing UI components
+(`reviewers`), and one with only backend components (`reviewers-backend`). The
+UI plugin is only compatible with the GWT UI, and does not work with PolyGerrit.
+Both build instructions will work with either `reviewers` or
+`reviewers-backend`.
+
 How to build the Gerrit Plugin API is described in the [Gerrit
 documentation](../../../Documentation/dev-bazel.html#_extension_and_plugin_api_jar_files).
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index cefe409..0b94f0e 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -7,24 +7,15 @@
 ```
   [reviewers]
     enableREST = true
-    enableUI = false
-    ignoreDrafts = true
     suggestOnly = false
+    ignoreWip = false
+    ignorePrivate = false
 ```
 
 reviewers.enableREST
 :	Enable the REST API. When set to false, the REST API is not available.
 	Defaults to true.
 
-reviewers.enableUI
-:	Enable the UI.  When set to false, the 'Reviewers' menu is not displayed
-	on the project screen. Defaults to true, or false when `enableREST` is false.
-
-reviewers.ignoreDrafts
-:	Ignore draft changes. When set to true draft changes are not considered when
-	adding reviewers. Defaults to false. To ignore drafts on a per-project basis
-	set this value to false and add "-status:draft" to filter in relevant projects.
-
 reviewers.suggestOnly
 :	Provide the configured reviewers as suggestions in the "Add Reviewer" dialog
 	instead of automatically adding them to the change. Only supports accounts;
@@ -33,6 +24,15 @@
 	list, set a higher value (like 1000) in `addReviewer.@PLUGIN@-reviewer-suggestion.weight`
 	in `gerrit.config`.
 
+reviewers.ignoreWip
+:	Ignore changes in WIP state. When set to true changes in WIP state are not
+	considered when adding reviewers. Defaults to true. To enable adding
+	reviewers on changes in WIP state set this value to false.
+
+reviewers.ignorePrivate
+:	Ignore changes in private state. When set to true changes in private state
+	are not considered when adding reviewers. Defaults to true. To enable
+	adding reviewers on changes in Private state set this value to false.
 Per project configuration of the @PLUGIN@ plugin is done in the
 `reviewers.config` file of the project. Missing values are inherited
 from the parent projects. This means a global default configuration can
@@ -50,8 +50,6 @@
   [filter "branch:stable-2.10"]
     reviewer = QAGroup
 
-  [filter "-status:draft"]
-    reviewer = DevGroup
 ```
 
 filter.\<filter\>.reviewer
diff --git a/src/main/resources/Documentation/rest-api.md b/src/main/resources/Documentation/rest-api.md
index 6fe3733..ebd04d4 100644
--- a/src/main/resources/Documentation/rest-api.md
+++ b/src/main/resources/Documentation/rest-api.md
@@ -56,6 +56,9 @@
 The change to reviewers must be provided in the request body inside
 a [ConfigReviewersInput](#config-reviewers-input) entity.
 
+Caller must be a member of a group that is granted the 'Modify Reviewers Config'
+capability (provided by this plugin) or be a Project Owner for the project.
+
 #### Request
 
 ```
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfigIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java
similarity index 92%
rename from src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfigIT.java
rename to src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java
index cf3db58..c966a6f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfigIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.GitUtil.fetch;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.FILENAME;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.KEY_REVIEWER;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.SECTION_FILTER;
+import static com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig.FILENAME;
+import static com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig.KEY_REVIEWER;
+import static com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig.SECTION_FILTER;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java
similarity index 92%
rename from src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
rename to src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java
index 2b1f67c..6036865 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java
@@ -12,15 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assert_;
 import static com.google.gerrit.acceptance.GitUtil.fetch;
 import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.FILENAME;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.KEY_REVIEWER;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.SECTION_FILTER;
+import static com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig.FILENAME;
+import static com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig.KEY_REVIEWER;
+import static com.googlesource.gerrit.plugins.reviewers.server.ReviewersConfig.SECTION_FILTER;
 import static java.util.stream.Collectors.toSet;
 
 import com.google.common.collect.ImmutableList;
@@ -52,7 +52,7 @@
   @Test
   public void addReviewers() throws Exception {
     RevCommit oldHead = getRemoteHead();
-    TestAccount user2 = accounts.user2();
+    TestAccount user2 = accountCreator.user2();
 
     Config cfg = new Config();
     cfg.setStringList(SECTION_FILTER, "*", KEY_REVIEWER, ImmutableList.of(user.email, user2.email));
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersResolverIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolverIT.java
similarity index 81%
rename from src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersResolverIT.java
rename to src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolverIT.java
index dd8d07b..c893a6c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersResolverIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolverIT.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.server;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -42,7 +42,6 @@
   public void testUploaderSkippedAsReviewer() throws Exception {
     Set<Account.Id> reviewers =
         resolver.resolve(
-            db,
             Collections.singleton(user.email),
             project,
             change,
@@ -54,7 +53,6 @@
   public void testAccountResolve() throws Exception {
     Set<Account.Id> reviewers =
         resolver.resolve(
-            db,
             ImmutableSet.of(user.email, admin.email),
             project,
             change,
@@ -65,18 +63,17 @@
   @Test
   public void testAccountGroupResolve() throws Exception {
     String group1 = createGroup("group1");
-    TestAccount foo = createAccount("foo", group1);
-    TestAccount bar = createAccount("bar", group1);
+    TestAccount foo = createTestAccount("foo", group1);
+    TestAccount bar = createTestAccount("bar", group1);
 
     String group2 = createGroup("group2");
-    TestAccount baz = createAccount("baz", group2);
-    TestAccount qux = createAccount("qux", group2);
+    TestAccount baz = createTestAccount("baz", group2);
+    TestAccount qux = createTestAccount("qux", group2);
 
-    TestAccount system = createAccount("system", "Administrators");
+    TestAccount system = createTestAccount("system", "Administrators");
 
     Set<Account.Id> reviewers =
         resolver.resolve(
-            db,
             ImmutableSet.of(system.email, group1, group2),
             project,
             change,
@@ -84,8 +81,8 @@
     assertThat(reviewers).containsExactly(system.id, foo.id, bar.id, baz.id, qux.id);
   }
 
-  private TestAccount createAccount(String name, String group) throws Exception {
+  private TestAccount createTestAccount(String name, String group) throws Exception {
     name = name(name);
-    return accounts.create(name, name + "@example.com", name + " full name", group);
+    return accountCreator.create(name, name + "@example.com", name + " full name", group);
   }
 }