RestApiModule: Move bindings for non-REST-endpoint-classes into a separate module

At Google we would like to have more control over the REST API that we
expose. For this we intent to install a custom RestApiModule that binds
only the REST endpoints that we need. If we do this the problem is that
we need to take care to bind all the non-REST-endpoint-classes
internally that are currently bound by RestApiModule, which can easily
go out of sync and hence is error-prone. To avoid this move the bindings
for non-REST-endpoint-classes out of RestApiModule into a separate
module. Then at Google we can just use that new module, while we replace
the RestApiModule that only bind REST endpoints.

Release-Notes: skip
Change-Id: Id46aa5f9a673938ced87dae8c569914ad70eed11
Signed-off-by: Edwin Kempin <ekempin@google.com>
Bug: Google b/289489297
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 0fb9310..7086447 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -202,6 +202,7 @@
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.ConflictsCacheImpl;
 import com.google.gerrit.server.quota.QuotaEnforcer;
+import com.google.gerrit.server.restapi.RestModule;
 import com.google.gerrit.server.restapi.change.OnPostReview;
 import com.google.gerrit.server.restapi.change.SuggestReviewers;
 import com.google.gerrit.server.restapi.group.GroupModule;
@@ -289,6 +290,7 @@
     install(new DefaultSubmitRuleModule());
     install(new IgnoreSelfApprovalRuleModule());
     install(new ReceiveCommitsModule());
+    install(new RestModule());
     install(new SshAddressesModule());
     install(new FileInfoJsonModule());
     install(ThreadLocalRequestContext.module());
diff --git a/java/com/google/gerrit/server/restapi/RestApiModule.java b/java/com/google/gerrit/server/restapi/RestApiModule.java
index dffcf44..73991c9 100644
--- a/java/com/google/gerrit/server/restapi/RestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/RestApiModule.java
@@ -24,6 +24,12 @@
 import com.google.gerrit.server.restapi.project.ProjectRestApiModule;
 import com.google.inject.AbstractModule;
 
+/**
+ * Module to bind REST API endpoints.
+ *
+ * <p>Classes that are needed by the REST layer, but which are not REST API endpoints, should be
+ * bound in {@link RestModule}.
+ */
 public class RestApiModule extends AbstractModule {
   @Override
   protected void configure() {
diff --git a/java/com/google/gerrit/server/restapi/RestModule.java b/java/com/google/gerrit/server/restapi/RestModule.java
new file mode 100644
index 0000000..0dcb4c8
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/RestModule.java
@@ -0,0 +1,133 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.restapi;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.gpg.PublicKeyStore;
+import com.google.gerrit.gpg.UnimplementedPublicKeyStoreProvider;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.UserInitiated;
+import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.change.AddReviewersOp;
+import com.google.gerrit.server.change.AddToAttentionSetOp;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.DeleteChangeOp;
+import com.google.gerrit.server.change.DeleteReviewerByEmailOp;
+import com.google.gerrit.server.change.DeleteReviewerOp;
+import com.google.gerrit.server.change.EmailReviewComments;
+import com.google.gerrit.server.change.PatchSetInserter;
+import com.google.gerrit.server.change.RebaseChangeOp;
+import com.google.gerrit.server.change.RemoveFromAttentionSetOp;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.SetCherryPickOp;
+import com.google.gerrit.server.change.SetCustomKeyedValuesOp;
+import com.google.gerrit.server.change.SetHashtagsOp;
+import com.google.gerrit.server.change.SetPrivateOp;
+import com.google.gerrit.server.change.SetTopicOp;
+import com.google.gerrit.server.change.WorkInProgressOp;
+import com.google.gerrit.server.comment.CommentContextLoader;
+import com.google.gerrit.server.config.GerritConfigListener;
+import com.google.gerrit.server.group.db.GroupsUpdate;
+import com.google.gerrit.server.project.RefValidationHelper;
+import com.google.gerrit.server.restapi.change.DeleteVoteOp;
+import com.google.gerrit.server.restapi.change.PostReviewOp;
+import com.google.gerrit.server.restapi.change.PreviewFix;
+import com.google.gerrit.server.restapi.project.CreateProject;
+import com.google.gerrit.server.restapi.project.ProjectNode;
+import com.google.gerrit.server.restapi.project.SetParent;
+import com.google.gerrit.server.util.AttentionSetEmail;
+import com.google.gerrit.server.validators.ProjectCreationValidationListener;
+import com.google.inject.Provides;
+import com.google.inject.multibindings.OptionalBinder;
+
+/**
+ * Module to bind classes that are needed but the REST layer, but which are not REST endpoints.
+ *
+ * <p>REST endpoints should be bound in {@link RestApiModule}.
+ */
+public class RestModule extends FactoryModule {
+  @Override
+  protected void configure() {
+    factory(AccountLoader.Factory.class);
+    factory(AddReviewersOp.Factory.class);
+    factory(AddToAttentionSetOp.Factory.class);
+    factory(AttentionSetEmail.Factory.class);
+    factory(ChangeInserter.Factory.class);
+    factory(ChangeResource.Factory.class);
+    factory(CommentContextLoader.Factory.class);
+    factory(DeleteChangeOp.Factory.class);
+    factory(DeleteReviewerByEmailOp.Factory.class);
+    factory(DeleteReviewerOp.Factory.class);
+    factory(DeleteVoteOp.Factory.class);
+    factory(EmailReviewComments.Factory.class);
+    factory(GroupsUpdate.Factory.class);
+    factory(PatchSetInserter.Factory.class);
+    factory(PostReviewOp.Factory.class);
+    factory(PreviewFix.Factory.class);
+    factory(ProjectNode.Factory.class);
+    factory(RebaseChangeOp.Factory.class);
+    factory(RefValidationHelper.Factory.class);
+    factory(RemoveFromAttentionSetOp.Factory.class);
+    factory(ReviewerResource.Factory.class);
+    factory(SetCherryPickOp.Factory.class);
+    factory(SetCustomKeyedValuesOp.Factory.class);
+    factory(SetHashtagsOp.Factory.class);
+    factory(SetPrivateOp.Factory.class);
+    factory(SetTopicOp.Factory.class);
+    factory(WorkInProgressOp.Factory.class);
+
+    DynamicSet.bind(binder(), GerritConfigListener.class).to(SetParent.class);
+    DynamicSet.bind(binder(), ProjectCreationValidationListener.class)
+        .to(CreateProject.ValidBranchListener.class);
+
+    OptionalBinder.newOptionalBinder(binder(), PublicKeyStore.class)
+        .setDefault()
+        .toProvider(UnimplementedPublicKeyStoreProvider.class);
+  }
+
+  @Provides
+  @ServerInitiated
+  AccountsUpdate provideServerInitiatedAccountsUpdate(
+      @AccountsUpdate.AccountsUpdateLoader.WithReindex
+          AccountsUpdate.AccountsUpdateLoader accountsUpdateFactory) {
+    return accountsUpdateFactory.createWithServerIdent();
+  }
+
+  @Provides
+  @UserInitiated
+  AccountsUpdate provideUserInitiatedAccountsUpdate(
+      @AccountsUpdate.AccountsUpdateLoader.WithReindex
+          AccountsUpdate.AccountsUpdateLoader accountsUpdateFactory,
+      IdentifiedUser currentUser) {
+    return accountsUpdateFactory.create(currentUser);
+  }
+
+  @Provides
+  @ServerInitiated
+  GroupsUpdate provideServerInitiatedGroupsUpdate(GroupsUpdate.Factory groupsUpdateFactory) {
+    return groupsUpdateFactory.createWithServerIdent();
+  }
+
+  @Provides
+  @UserInitiated
+  GroupsUpdate provideUserInitiatedGroupsUpdate(
+      GroupsUpdate.Factory groupsUpdateFactory, IdentifiedUser currentUser) {
+    return groupsUpdateFactory.create(currentUser);
+  }
+}
diff --git a/java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java b/java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java
index 821d16e..53edfdb 100644
--- a/java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java
@@ -23,14 +23,6 @@
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.gpg.PublicKeyStore;
-import com.google.gerrit.gpg.UnimplementedPublicKeyStoreProvider;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.ServerInitiated;
-import com.google.gerrit.server.UserInitiated;
-import com.google.gerrit.server.account.AccountsUpdate;
-import com.google.inject.Provides;
-import com.google.inject.multibindings.OptionalBinder;
 
 public class AccountRestApiModule extends RestApiModule {
   @Override
@@ -39,10 +31,6 @@
     bind(Capabilities.class);
     bind(StarredChanges.Create.class);
 
-    OptionalBinder.newOptionalBinder(binder(), PublicKeyStore.class)
-        .setDefault()
-        .toProvider(UnimplementedPublicKeyStoreProvider.class);
-
     DynamicMap.mapOf(binder(), ACCOUNT_KIND);
     DynamicMap.mapOf(binder(), CAPABILITY_KIND);
     DynamicMap.mapOf(binder(), EMAIL_KIND);
@@ -113,21 +101,4 @@
     // The gpgkeys REST endpoints are bound via GpgApiModule.
     // The oauthtoken REST endpoint is bound via OAuthRestModule.
   }
-
-  @Provides
-  @ServerInitiated
-  AccountsUpdate provideServerInitiatedAccountsUpdate(
-      @AccountsUpdate.AccountsUpdateLoader.WithReindex
-          AccountsUpdate.AccountsUpdateLoader accountsUpdateFactory) {
-    return accountsUpdateFactory.createWithServerIdent();
-  }
-
-  @Provides
-  @UserInitiated
-  AccountsUpdate provideUserInitiatedAccountsUpdate(
-      @AccountsUpdate.AccountsUpdateLoader.WithReindex
-          AccountsUpdate.AccountsUpdateLoader accountsUpdateFactory,
-      IdentifiedUser currentUser) {
-    return accountsUpdateFactory.create(currentUser);
-  }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java b/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
index 6b121f6..2ac24c6 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
@@ -29,29 +29,8 @@
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.server.account.AccountLoader;
-import com.google.gerrit.server.change.AddReviewersOp;
-import com.google.gerrit.server.change.AddToAttentionSetOp;
-import com.google.gerrit.server.change.ChangeInserter;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.DeleteChangeOp;
-import com.google.gerrit.server.change.DeleteReviewerByEmailOp;
-import com.google.gerrit.server.change.DeleteReviewerOp;
-import com.google.gerrit.server.change.EmailReviewComments;
-import com.google.gerrit.server.change.PatchSetInserter;
-import com.google.gerrit.server.change.RebaseChangeOp;
-import com.google.gerrit.server.change.RemoveFromAttentionSetOp;
-import com.google.gerrit.server.change.ReviewerResource;
-import com.google.gerrit.server.change.SetCherryPickOp;
-import com.google.gerrit.server.change.SetCustomKeyedValuesOp;
-import com.google.gerrit.server.change.SetHashtagsOp;
-import com.google.gerrit.server.change.SetPrivateOp;
-import com.google.gerrit.server.change.SetTopicOp;
-import com.google.gerrit.server.change.WorkInProgressOp;
-import com.google.gerrit.server.comment.CommentContextLoader;
 import com.google.gerrit.server.restapi.change.Reviewed.DeleteReviewed;
 import com.google.gerrit.server.restapi.change.Reviewed.PutReviewed;
-import com.google.gerrit.server.util.AttentionSetEmail;
 
 public class ChangeRestApiModule extends RestApiModule {
   @Override
@@ -211,30 +190,5 @@
     post(VOTE_KIND, "delete").to(DeleteVote.class);
 
     post(CHANGE_KIND, "wip").to(SetWorkInProgress.class);
-
-    factory(AccountLoader.Factory.class);
-    factory(AddReviewersOp.Factory.class);
-    factory(AddToAttentionSetOp.Factory.class);
-    factory(AttentionSetEmail.Factory.class);
-    factory(ChangeInserter.Factory.class);
-    factory(ChangeResource.Factory.class);
-    factory(CommentContextLoader.Factory.class);
-    factory(DeleteChangeOp.Factory.class);
-    factory(DeleteReviewerByEmailOp.Factory.class);
-    factory(DeleteReviewerOp.Factory.class);
-    factory(DeleteVoteOp.Factory.class);
-    factory(EmailReviewComments.Factory.class);
-    factory(PatchSetInserter.Factory.class);
-    factory(PostReviewOp.Factory.class);
-    factory(PreviewFix.Factory.class);
-    factory(RebaseChangeOp.Factory.class);
-    factory(RemoveFromAttentionSetOp.Factory.class);
-    factory(ReviewerResource.Factory.class);
-    factory(SetCherryPickOp.Factory.class);
-    factory(SetCustomKeyedValuesOp.Factory.class);
-    factory(SetHashtagsOp.Factory.class);
-    factory(SetPrivateOp.Factory.class);
-    factory(SetTopicOp.Factory.class);
-    factory(WorkInProgressOp.Factory.class);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewOp.java b/java/com/google/gerrit/server/restapi/change/PostReviewOp.java
index 8479d91..a47e179 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewOp.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewOp.java
@@ -97,7 +97,7 @@
 import org.eclipse.jgit.lib.Config;
 
 public class PostReviewOp implements BatchUpdateOp {
-  interface Factory {
+  public interface Factory {
     PostReviewOp create(
         ProjectState projectState, PatchSet.Id psId, ReviewInput in, Account.Id reviewerId);
   }
diff --git a/java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java b/java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java
index 1eaa6a2..f115374 100644
--- a/java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java
@@ -20,17 +20,12 @@
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.ServerInitiated;
-import com.google.gerrit.server.UserInitiated;
-import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.restapi.group.AddMembers.CreateMember;
 import com.google.gerrit.server.restapi.group.AddMembers.UpdateMember;
 import com.google.gerrit.server.restapi.group.AddSubgroups.CreateSubgroup;
 import com.google.gerrit.server.restapi.group.AddSubgroups.UpdateSubgroup;
 import com.google.gerrit.server.restapi.group.DeleteMembers.DeleteMember;
 import com.google.gerrit.server.restapi.group.DeleteSubgroups.DeleteSubgroup;
-import com.google.inject.Provides;
 
 public class GroupRestApiModule extends RestApiModule {
 
@@ -77,20 +72,5 @@
     put(GROUP_KIND, "options").to(PutOptions.class);
     get(GROUP_KIND, "owner").to(GetOwner.class);
     put(GROUP_KIND, "owner").to(PutOwner.class);
-
-    factory(GroupsUpdate.Factory.class);
-  }
-
-  @Provides
-  @ServerInitiated
-  GroupsUpdate provideServerInitiatedGroupsUpdate(GroupsUpdate.Factory groupsUpdateFactory) {
-    return groupsUpdateFactory.createWithServerIdent();
-  }
-
-  @Provides
-  @UserInitiated
-  GroupsUpdate provideUserInitiatedGroupsUpdate(
-      GroupsUpdate.Factory groupsUpdateFactory, IdentifiedUser currentUser) {
-    return groupsUpdateFactory.create(currentUser);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index cfdadd9..04819d8 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -227,7 +227,7 @@
     return branch;
   }
 
-  static class ValidBranchListener implements ProjectCreationValidationListener {
+  public static class ValidBranchListener implements ProjectCreationValidationListener {
     @Override
     public void validateNewProject(CreateProjectArgs args) throws ValidationException {
       for (String branch : args.branch) {
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java b/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
index a680ec4..5a38766 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
@@ -25,12 +25,8 @@
 import static com.google.gerrit.server.project.TagResource.TAG_KIND;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.server.config.GerritConfigListener;
-import com.google.gerrit.server.project.RefValidationHelper;
 import com.google.gerrit.server.restapi.change.CherryPickCommit;
-import com.google.gerrit.server.validators.ProjectCreationValidationListener;
 
 public class ProjectRestApiModule extends RestApiModule {
 
@@ -49,10 +45,6 @@
     DynamicMap.mapOf(binder(), SUBMIT_REQUIREMENT_KIND);
     DynamicMap.mapOf(binder(), TAG_KIND);
 
-    DynamicSet.bind(binder(), GerritConfigListener.class).to(SetParent.class);
-    DynamicSet.bind(binder(), ProjectCreationValidationListener.class)
-        .to(CreateProject.ValidBranchListener.class);
-
     create(PROJECT_KIND).to(CreateProject.class);
     get(PROJECT_KIND).to(GetProject.class);
     put(PROJECT_KIND).to(PutProject.class);
@@ -129,9 +121,6 @@
     delete(TAG_KIND).to(DeleteTag.class);
 
     post(PROJECT_KIND, "tags:delete").to(DeleteTags.class);
-
-    factory(RefValidationHelper.Factory.class);
-    factory(ProjectNode.Factory.class);
   }
 
   /** Separately bind batch functionality. */