Merge branch 'stable-2.11' into stable-2.12

* stable-2.11:
  CurrentSchemaVersion: Allow to use it in plugins
  Use REST implementation to list members for label with group operator
  InitAdminUser: Don't assume the group ID of the Administrators group

Change-Id: I1a24005e3eb0edaca259f65ff15258e2107112e6
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
index a09dcd5..40a07b4 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
+import com.google.gerrit.reviewdb.client.AccountGroupName;
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -101,9 +102,11 @@
           a.setPreferredEmail(email);
           db.accounts().insert(Collections.singleton(a));
 
+          AccountGroupName adminGroup = db.accountGroupNames().get(
+              new AccountGroup.NameKey("Administrators"));
           AccountGroupMember m =
               new AccountGroupMember(new AccountGroupMember.Key(id,
-                  new AccountGroup.Id(1)));
+                  adminGroup.getId()));
           db.accountGroupMembers().insert(Collections.singleton(m));
 
           if (sshKey != null) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java
index 183bb1e..cba5d41 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java
@@ -26,7 +26,7 @@
     private static final String VALUE = "X";
 
     @Column(id = 1, length = 1)
-    protected String one = VALUE;
+    public String one = VALUE;
 
     public Key() {
     }
@@ -50,12 +50,12 @@
   }
 
   @Column(id = 1)
-  protected Key singleton;
+  public Key singleton;
 
   /** Current version number of the schema. */
   @Column(id = 2)
   public transient int versionNbr;
 
-  protected CurrentSchemaVersion() {
+  public CurrentSchemaVersion() {
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
index aab9efe..405cdf1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
@@ -58,8 +58,9 @@
     this.accountLoader = accountLoaderFactory.create(true);
   }
 
-  public void setRecursive(boolean recursive) {
+  public ListMembers setRecursive(boolean recursive) {
     this.recursive = recursive;
+    return this;
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 03c2bdb..a4cff07 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -17,18 +17,16 @@
 import static com.google.gerrit.server.query.change.ChangeData.asChanges;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.gerrit.common.data.GroupDetail;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -42,8 +40,6 @@
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.account.VersionedAccountDestinations;
 import com.google.gerrit.server.account.VersionedAccountQueries;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.GroupDetailFactory;
 import com.google.gerrit.server.change.ChangeTriplet;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
@@ -52,6 +48,7 @@
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
+import com.google.gerrit.server.group.ListMembers;
 import com.google.gerrit.server.index.ChangeIndex;
 import com.google.gerrit.server.index.FieldDef;
 import com.google.gerrit.server.index.IndexCollection;
@@ -155,7 +152,6 @@
     final Provider<InternalChangeQuery> queryProvider;
     final IndexRewriter rewriter;
     final IdentifiedUser.GenericFactory userFactory;
-    final GroupDetailFactory.Factory groupDetailFactory;
     final CapabilityControl.Factory capabilityControlFactory;
     final ChangeControl.GenericFactory changeControlGenericFactory;
     final ChangeData.Factory changeDataFactory;
@@ -168,14 +164,14 @@
     final PatchListCache patchListCache;
     final GitRepositoryManager repoManager;
     final ProjectCache projectCache;
-    final GroupCache groupCache;
     final Provider<ListChildProjects> listChildProjects;
     final SubmitStrategyFactory submitStrategyFactory;
     final ConflictsCache conflictsCache;
     final TrackingFooters trackingFooters;
-    final boolean allowsDrafts;
     final ChangeIndex index;
     final IndexConfig indexConfig;
+    final Provider<ListMembers> listMembers;
+    final boolean allowsDrafts;
 
     private final Provider<CurrentUser> self;
 
@@ -187,7 +183,6 @@
         IdentifiedUser.GenericFactory userFactory,
         Provider<CurrentUser> self,
         CapabilityControl.Factory capabilityControlFactory,
-        GroupDetailFactory.Factory groupDetailFactory,
         ChangeControl.GenericFactory changeControlGenericFactory,
         ChangeData.Factory changeDataFactory,
         FieldDef.FillArgs fillArgs,
@@ -199,22 +194,22 @@
         PatchListCache patchListCache,
         GitRepositoryManager repoManager,
         ProjectCache projectCache,
-        GroupCache groupCache,
         Provider<ListChildProjects> listChildProjects,
         IndexCollection indexes,
         SubmitStrategyFactory submitStrategyFactory,
         ConflictsCache conflictsCache,
         TrackingFooters trackingFooters,
         IndexConfig indexConfig,
+        Provider<ListMembers> listMembers,
         @GerritServerConfig Config cfg) {
       this(db, queryProvider, rewriter, userFactory, self,
-          capabilityControlFactory, groupDetailFactory, changeControlGenericFactory,
+          capabilityControlFactory, changeControlGenericFactory,
           changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend,
           allProjectsName, allUsersName, patchListCache, repoManager,
-          projectCache, groupCache, listChildProjects, submitStrategyFactory,
+          projectCache, listChildProjects, submitStrategyFactory,
           conflictsCache, trackingFooters,
           indexes != null ? indexes.getSearchIndex() : null,
-          indexConfig,
+          indexConfig, listMembers,
           cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true));
     }
 
@@ -225,7 +220,6 @@
         IdentifiedUser.GenericFactory userFactory,
         Provider<CurrentUser> self,
         CapabilityControl.Factory capabilityControlFactory,
-        GroupDetailFactory.Factory groupDetailFactory,
         ChangeControl.GenericFactory changeControlGenericFactory,
         ChangeData.Factory changeDataFactory,
         FieldDef.FillArgs fillArgs,
@@ -237,13 +231,13 @@
         PatchListCache patchListCache,
         GitRepositoryManager repoManager,
         ProjectCache projectCache,
-        GroupCache groupCache,
         Provider<ListChildProjects> listChildProjects,
         SubmitStrategyFactory submitStrategyFactory,
         ConflictsCache conflictsCache,
         TrackingFooters trackingFooters,
         ChangeIndex index,
         IndexConfig indexConfig,
+        Provider<ListMembers> listMembers,
         boolean allowsDrafts) {
      this.db = db;
      this.queryProvider = queryProvider;
@@ -251,7 +245,6 @@
      this.userFactory = userFactory;
      this.self = self;
      this.capabilityControlFactory = capabilityControlFactory;
-     this.groupDetailFactory = groupDetailFactory;
      this.changeControlGenericFactory = changeControlGenericFactory;
      this.changeDataFactory = changeDataFactory;
      this.fillArgs = fillArgs;
@@ -263,24 +256,25 @@
      this.patchListCache = patchListCache;
      this.repoManager = repoManager;
      this.projectCache = projectCache;
-     this.groupCache = groupCache;
      this.listChildProjects = listChildProjects;
      this.submitStrategyFactory = submitStrategyFactory;
      this.conflictsCache = conflictsCache;
      this.trackingFooters = trackingFooters;
-     this.allowsDrafts = allowsDrafts;
      this.index = index;
      this.indexConfig = indexConfig;
+     this.listMembers = listMembers;
+     this.allowsDrafts = allowsDrafts;
     }
 
     Arguments asUser(CurrentUser otherUser) {
       return new Arguments(db, queryProvider, rewriter, userFactory,
           Providers.of(otherUser),
-          capabilityControlFactory, groupDetailFactory, changeControlGenericFactory,
+          capabilityControlFactory, changeControlGenericFactory,
           changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend,
           allProjectsName, allUsersName, patchListCache, repoManager,
-          projectCache, groupCache, listChildProjects, submitStrategyFactory,
-          conflictsCache, trackingFooters, index, indexConfig, allowsDrafts);
+          projectCache, listChildProjects, submitStrategyFactory,
+          conflictsCache, trackingFooters, index, indexConfig, listMembers,
+          allowsDrafts);
     }
 
     Arguments asUser(Account.Id otherId) {
@@ -603,8 +597,15 @@
 
     // expand a group predicate into multiple user predicates
     if (group != null) {
-      Set<Account.Id> allMembers = getMemberIds(
-          group, new HashSet<AccountGroup.UUID>());
+      Set<Account.Id> allMembers =
+          new HashSet<>(Lists.transform(
+              args.listMembers.get().setRecursive(true).apply(group),
+              new Function<AccountInfo, Account.Id>() {
+                @Override
+                public Account.Id apply(AccountInfo accountInfo) {
+                  return new Account.Id(accountInfo._accountId);
+                }
+              }));
       int maxTerms = args.indexConfig.maxLimit();
       if (allMembers.size() > maxTerms) {
         // limit the number of query terms otherwise Gerrit will barf
@@ -951,39 +952,6 @@
     return g;
   }
 
-  private Set<Account.Id> getMemberIds(AccountGroup.UUID groupUUID,
-      Set<AccountGroup.UUID> seenGroups) throws OrmException {
-    seenGroups.add(groupUUID);
-
-    Set<Account.Id> members = new HashSet<>();
-    AccountGroup group = args.groupCache.get(groupUUID);
-    if (group != null) {
-      try {
-        GroupDetail groupDetail =
-            args.groupDetailFactory.create(group.getId()).call();
-        if (groupDetail.members != null) {
-          for (AccountGroupMember m : groupDetail.members) {
-            if (!members.contains(m.getAccountId())) {
-              members.add(m.getAccountId());
-            }
-          }
-        }
-        // Get members of subgroups
-        if (groupDetail.includes != null) {
-          for (AccountGroupById includedGroup : groupDetail.includes) {
-            if (!seenGroups.contains(includedGroup.getIncludeUUID())) {
-              members.addAll(
-                  getMemberIds(includedGroup.getIncludeUUID(), seenGroups));
-            }
-          }
-        }
-      } catch (NoSuchGroupException e) {
-        // The included group is not visible
-      }
-    }
-    return members;
-  }
-
   private List<Change> parseChange(String value) throws OrmException,
       QueryParseException {
     if (PAT_LEGACY_ID.matcher(value).matches()) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
index 829b28e..63ae818 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -25,10 +25,9 @@
     super(
         new FakeQueryBuilder.Definition<>(
           FakeQueryBuilder.class),
-        new ChangeQueryBuilder.Arguments(null, null, null, null,
-            null, null, null, null, null, null, null, null, null,
-            null, null, null, null, null, null, null, indexes, null,
-            null, null, null, null));
+        new ChangeQueryBuilder.Arguments(null, null, null, null, null, null,
+            null, null, null, null, null, null, null, null, null, null, null,
+            null, indexes, null, null, null, null, null, null));
   }
 
   @Operator