Allow to hide download schemes from the REST API and  UI

Sometimes, e.g. during deprecation scenarios, not showing a download
scheme in the UI, but still allowing to use it, would be helpful.

Release-Notes: Allow to hide download schemes from the UI
Change-Id: I50b3759f3e1be750423c8077969e553936f20212
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index ced35e7..33f0e8b 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2087,6 +2087,7 @@
   scheme = anon_http
   scheme = anon_git
   scheme = repo
+  hide = ssh
 ----
 
 The download section configures the allowed download methods.
@@ -2155,6 +2156,17 @@
 If `download.scheme` is not specified, SSH, HTTP and Anonymous HTTP
 downloads are allowed.
 
+[[download.hide]]download.hide::
++
+Schemes that can be used to download changes, but will not be advertised
+in the UI. This can be any scheme that can be configured in <<download.scheme>>.
++
+This is mostly useful in a deprecation scenario during a time where using
+a scheme is discouraged, but has to be supported until all clients have
+migrated to use a different scheme.
++
+By default, no scheme will be hidden in the UI.
+
 [[download.checkForHiddenChangeRefs]]download.checkForHiddenChangeRefs::
 +
 Whether the download commands should be adapted when the change refs
diff --git a/java/com/google/gerrit/extensions/config/DownloadScheme.java b/java/com/google/gerrit/extensions/config/DownloadScheme.java
index 96b5878..15801d4 100644
--- a/java/com/google/gerrit/extensions/config/DownloadScheme.java
+++ b/java/com/google/gerrit/extensions/config/DownloadScheme.java
@@ -34,4 +34,7 @@
 
   /** Returns whether the download scheme is enabled */
   public abstract boolean isEnabled();
+
+  /** Returns whether the download scheme is hidden in the UI */
+  public abstract boolean isHidden();
 }
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index 7d40f06..0170f35 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -250,6 +250,7 @@
       String schemeName = e.getExportName();
       DownloadScheme scheme = e.getProvider().get();
       if (!scheme.isEnabled()
+          || scheme.isHidden()
           || (scheme.isAuthRequired() && !userProvider.get().isIdentifiedUser())) {
         continue;
       }
diff --git a/java/com/google/gerrit/server/config/DownloadConfig.java b/java/com/google/gerrit/server/config/DownloadConfig.java
index 496808a..f75c387 100644
--- a/java/com/google/gerrit/server/config/DownloadConfig.java
+++ b/java/com/google/gerrit/server/config/DownloadConfig.java
@@ -23,8 +23,11 @@
 import com.google.inject.Singleton;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import org.eclipse.jgit.lib.Config;
 
 /**
@@ -38,6 +41,7 @@
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private final ImmutableSet<String> downloadSchemes;
+  private final ImmutableSet<String> hiddenSchemes;
   private final ImmutableSet<DownloadCommand> downloadCommands;
   private final ImmutableSet<ArchiveFormatInternal> archiveFormats;
 
@@ -62,6 +66,10 @@
       downloadSchemes = normalized.build();
     }
 
+    Set<String> hidden = new HashSet<>(Arrays.asList(cfg.getStringList("download", null, "hide")));
+    hidden.retainAll(downloadSchemes);
+    hiddenSchemes = ImmutableSet.copyOf(hidden);
+
     DownloadCommand[] downloadCommandValues = DownloadCommand.values();
     List<DownloadCommand> allCommands =
         ConfigUtil.getEnumList(cfg, "download", null, "command", downloadCommandValues, null);
@@ -108,6 +116,11 @@
     return downloadSchemes;
   }
 
+  /** Scheme hidden in the UI. */
+  public ImmutableSet<String> getHiddenSchemes() {
+    return hiddenSchemes;
+  }
+
   /** Command used to download. */
   public ImmutableSet<DownloadCommand> getDownloadCommands() {
     return downloadCommands;
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index 09052a6..398cad9 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -244,7 +244,7 @@
     downloadSchemes.runEach(
         extension -> {
           DownloadScheme scheme = extension.get();
-          if (scheme.isEnabled() && scheme.getUrl("${project}") != null) {
+          if (scheme.isEnabled() && !scheme.isHidden() && scheme.getUrl("${project}") != null) {
             info.schemes.put(extension.getExportName(), getDownloadSchemeInfo(scheme));
           }
         });
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index aa8615b..08ae9a6 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -245,5 +245,10 @@
     public boolean isEnabled() {
       return true;
     }
+
+    @Override
+    public boolean isHidden() {
+      return false;
+    }
   }
 }
diff --git a/plugins/download-commands b/plugins/download-commands
index 71331e1..ff885b0 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit 71331e15af5a62ee7b13dee6ebdadf23d7e75a40
+Subproject commit ff885b0872d4fdd2147b371234513b0c58b6fabb