Introduce API for retrieving the remote config

Extend the ReplicationRemotesApi for retrieving the
configuration associated with multiple replication remotes.

NOTE: The configuration would not include passwords
for the sake of not exposing them when not necessarily needed.

Change-Id: I26b6f098dd7b6331adf929315b2cc1cebdf0eb4a
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/MergedConfigResource.java b/src/main/java/com/googlesource/gerrit/plugins/replication/MergedConfigResource.java
index 315c4a6..58a10f0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/MergedConfigResource.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/MergedConfigResource.java
@@ -78,7 +78,7 @@
     return baseVersion + overrides.get().getVersion();
   }
 
-  private boolean noOverrides() {
+  boolean noOverrides() {
     return overrides == null || overrides.get() == null;
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiImpl.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiImpl.java
index dd4d5b0..e4ef318 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiImpl.java
@@ -14,34 +14,42 @@
 
 package com.googlesource.gerrit.plugins.replication;
 
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.securestore.SecureStore;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.replication.api.ConfigResource;
-import com.googlesource.gerrit.plugins.replication.api.ReplicationConfigOverrides;
 import com.googlesource.gerrit.plugins.replication.api.ReplicationRemotesApi;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 import org.eclipse.jgit.lib.Config;
 
 @Singleton
 class ReplicationRemotesApiImpl implements ReplicationRemotesApi {
 
   private final SecureStore secureStore;
-  private final Provider<ConfigResource> baseConfigProvider;
-  private final DynamicItem<ReplicationConfigOverrides> configOverridesItem;
+  private final MergedConfigResource mergedConfigResource;
 
   @Inject
-  ReplicationRemotesApiImpl(
-      SecureStore secureStore,
-      Provider<ConfigResource> baseConfigProvider,
-      @Nullable DynamicItem<ReplicationConfigOverrides> configOverridesItem) {
+  ReplicationRemotesApiImpl(SecureStore secureStore, MergedConfigResource mergedConfigResource) {
     this.secureStore = secureStore;
-    this.baseConfigProvider = baseConfigProvider;
-    this.configOverridesItem = configOverridesItem;
+    this.mergedConfigResource = mergedConfigResource;
+  }
+
+  @Override
+  public Config get(String... remoteNames) {
+    Config replicationConfig = mergedConfigResource.getConfig();
+    Config remoteConfig = new Config();
+    for (String remoteName : remoteNames) {
+      Set<String> configNames = replicationConfig.getNames("remote", remoteName);
+      for (String configName : configNames) {
+        String[] values = replicationConfig.getStringList("remote", remoteName, configName);
+        if (values.length > 0) {
+          remoteConfig.setStringList("remote", remoteName, configName, Arrays.asList(values));
+        }
+      }
+    }
+    return remoteConfig;
   }
 
   @Override
@@ -54,11 +62,7 @@
     SeparatedRemoteConfigs configs = onlyRemoteSectionsWithSeparatedPasswords(remoteConfig);
     persistRemotesPasswords(configs);
 
-    if (hasConfigOverrides()) {
-      configOverridesItem.get().update(configs.remotes);
-    } else {
-      baseConfigProvider.get().update(configs.remotes);
-    }
+    mergedConfigResource.update(configs.remotes);
   }
 
   private SeparatedRemoteConfigs onlyRemoteSectionsWithSeparatedPasswords(Config configUpdates) {
@@ -85,10 +89,6 @@
     }
   }
 
-  private boolean hasConfigOverrides() {
-    return configOverridesItem != null && configOverridesItem.get() != null;
-  }
-
   private static class SeparatedRemoteConfigs {
     private final Config remotes = new Config();
     private final Config passwords = new Config();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/api/ReplicationRemotesApi.java b/src/main/java/com/googlesource/gerrit/plugins/replication/api/ReplicationRemotesApi.java
index fde2abc..76d6d35 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/api/ReplicationRemotesApi.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/api/ReplicationRemotesApi.java
@@ -28,6 +28,19 @@
 public interface ReplicationRemotesApi {
 
   /**
+   * Retrieves the configuration for a remote by name.
+   *
+   * <p>Builds a JGit {@link Config} with the remote's configuration obtained by parsing the
+   * replication configuration sources.
+   *
+   * <p>NOTE: The remotes secrets are excluded for security reasons.
+   *
+   * @param remoteNames the remote names to retrieve.
+   * @return {@link Config} associated with the remoteName(s)
+   */
+  Config get(String... remoteNames);
+
+  /**
    * Adds or updates the remote configuration for the replication plugin.
    *
    * <p>Provided JGit {@link Config} object should contain at least one named <em>remote</em>
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiTest.java
index 3aa97e8..a806875 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationRemotesApiTest.java
@@ -19,20 +19,23 @@
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import com.google.common.io.MoreFiles;
 import com.google.common.truth.StringSubject;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.securestore.SecureStore;
-import com.google.inject.util.Providers;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.replication.api.ConfigResource;
 import com.googlesource.gerrit.plugins.replication.api.ReplicationConfigOverrides;
 import com.googlesource.gerrit.plugins.replication.api.ReplicationRemotesApi;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 import org.eclipse.jgit.lib.Config;
 import org.junit.After;
 import org.junit.Before;
@@ -43,12 +46,36 @@
   private Path testSite;
   private SecureStore secureStoreMock;
   private FileConfigResource baseConfig;
+  private Injector testInjector;
+  private AtomicReference<ReplicationConfigOverrides> testOverrides;
+  private String url1;
+  private String remoteName1;
+  private String url2;
+  private String remoteName2;
 
   @Before
   public void setUp() throws Exception {
     testSite = Files.createTempDirectory("replicationRemotesUpdateTest");
     secureStoreMock = mock(SecureStore.class);
     baseConfig = new FileConfigResource(new SitePaths(testSite));
+    testOverrides = new AtomicReference<>(new TestReplicationConfigOverrides());
+    testInjector =
+        Guice.createInjector(
+            new AbstractModule() {
+              @Override
+              protected void configure() {
+                bind(ConfigResource.class).toInstance(baseConfig);
+                bind(SecureStore.class).toInstance(secureStoreMock);
+                bind(ReplicationRemotesApi.class).to(ReplicationRemotesApiImpl.class);
+                DynamicItem.itemOf(binder(), ReplicationConfigOverrides.class);
+                DynamicItem.bind(binder(), ReplicationConfigOverrides.class)
+                    .toProvider(testOverrides::get);
+              }
+            });
+    url1 = "fake_url1";
+    remoteName1 = "site1";
+    url2 = "fake_url2";
+    remoteName2 = "site2";
   }
 
   @After
@@ -59,7 +86,7 @@
   @Test
   public void shouldThrowWhenNoRemotesInTheUpdate() {
     Config update = new Config();
-    ReplicationRemotesApi objectUnderTest = newReplicationConfigUpdater();
+    ReplicationRemotesApi objectUnderTest = getReplicationRemotesApi();
 
     assertThrows(IllegalArgumentException.class, () -> objectUnderTest.update(update));
 
@@ -69,64 +96,83 @@
   }
 
   @Test
-  public void addRemoteSectionToBaseConfigWhenNoOverrides() throws Exception {
-    String url = "fake_url";
-    Config update = new Config();
-    setRemoteSite(update, "url", url);
-    ReplicationRemotesApi objectUnderTest = newReplicationConfigUpdater();
-
-    objectUnderTest.update(update);
-
-    assertRemoteSite(baseConfig.getConfig(), "url").isEqualTo(url);
+  public void shouldReturnEmptyConfigWhenNoRemotes() {
+    ReplicationRemotesApi objectUnderTest = getReplicationRemotesApi();
+    assertThat(objectUnderTest.get("site").getSections()).isEmpty();
   }
 
   @Test
-  public void addRemoteSectionToBaseOverridesConfig() throws Exception {
-    TestReplicationConfigOverrides testOverrides = new TestReplicationConfigOverrides();
-    String url = "fake_url";
+  public void addRemoteSectionsToBaseConfigWhenNoOverrides() throws Exception {
+    testOverrides.set(null);
+
     Config update = new Config();
-    setRemoteSite(update, "url", url);
-    ReplicationRemotesApi objectUnderTest = newReplicationConfigUpdater(testOverrides);
+    setRemoteSite(update, remoteName1, "url", url1);
+    setRemoteSite(update, remoteName2, "url", url2);
+    ReplicationRemotesApi objectUnderTest = getReplicationRemotesApi();
 
     objectUnderTest.update(update);
 
-    assertRemoteSite(testOverrides.getConfig(), "url").isEqualTo(url);
-    assertRemoteSite(baseConfig.getConfig(), "url").isNull();
+    assertRemoteSite(baseConfig.getConfig(), remoteName1, "url").isEqualTo(url1);
+    assertRemoteSite(baseConfig.getConfig(), remoteName2, "url").isEqualTo(url2);
   }
 
   @Test
-  public void encryptPassword() throws Exception {
-    TestReplicationConfigOverrides testOverrides = new TestReplicationConfigOverrides();
+  public void shouldReturnRemotesFromBaseConfigWhenNoOverrides() {
+    ReplicationRemotesApi objectUnderTest = getReplicationRemotesApi();
+
+    baseConfig.getConfig().setString("remote", remoteName1, "url", url1);
+    baseConfig.getConfig().setString("remote", remoteName2, "url", url2);
+
+    Config remotesConfig = objectUnderTest.get(remoteName1, remoteName2);
+    assertRemoteSite(remotesConfig, remoteName1, "url").isEqualTo(url1);
+    assertRemoteSite(remotesConfig, remoteName2, "url").isEqualTo(url2);
+  }
+
+  @Test
+  public void addRemotesSectionToBaseOverridesConfig() throws Exception {
+    Config update = new Config();
+    setRemoteSite(update, remoteName1, "url", url1);
+    setRemoteSite(update, remoteName2, "url", url2);
+    ReplicationRemotesApi objectUnderTest = getReplicationRemotesApi();
+
+    objectUnderTest.update(update);
+
+    assertRemoteSite(testOverrides.get().getConfig(), remoteName1, "url").isEqualTo(url1);
+    assertRemoteSite(testOverrides.get().getConfig(), remoteName2, "url").isEqualTo(url2);
+    assertRemoteSite(baseConfig.getConfig(), remoteName1, "url").isNull();
+    assertRemoteSite(baseConfig.getConfig(), remoteName2, "url").isNull();
+
+    Config remotesConfig = objectUnderTest.get(remoteName1, remoteName2);
+    assertRemoteSite(remotesConfig, remoteName1, "url").isEqualTo(url1);
+    assertRemoteSite(remotesConfig, remoteName2, "url").isEqualTo(url2);
+  }
+
+  @Test
+  public void shouldEncryptPasswordButNotStoreInConfig() throws Exception {
     Config update = new Config();
     String password = "my_secret_password";
-    setRemoteSite(update, "password", password);
-    ReplicationRemotesApi objectUnderTest = newReplicationConfigUpdater(testOverrides);
+    String remoteName = "site";
+    setRemoteSite(update, remoteName, "password", password);
+    ReplicationRemotesApi objectUnderTest = getReplicationRemotesApi();
 
     objectUnderTest.update(update);
 
-    verify(secureStoreMock).setList("remote", "site", "password", List.of(password));
-    assertRemoteSite(baseConfig.getConfig(), "password").isNull();
-    assertRemoteSite(testOverrides.getConfig(), "password").isNull();
+    verify(secureStoreMock).setList("remote", remoteName, "password", List.of(password));
+    assertRemoteSite(baseConfig.getConfig(), remoteName, "password").isNull();
+    assertRemoteSite(testOverrides.get().getConfig(), remoteName, "password").isNull();
+    assertRemoteSite(objectUnderTest.get(remoteName), remoteName, "password").isNull();
   }
 
-  private ReplicationRemotesApi newReplicationConfigUpdater() {
-    return newReplicationConfigUpdater(null);
+  private void setRemoteSite(Config config, String remoteName, String name, String value) {
+    config.setString("remote", remoteName, name, value);
   }
 
-  private void setRemoteSite(Config config, String name, String value) {
-    config.setString("remote", "site", name, value);
+  private StringSubject assertRemoteSite(Config config, String remoteName, String name) {
+    return assertThat(config.getString("remote", remoteName, name));
   }
 
-  private StringSubject assertRemoteSite(Config config, String name) {
-    return assertThat(config.getString("remote", "site", name));
-  }
-
-  private ReplicationRemotesApi newReplicationConfigUpdater(ReplicationConfigOverrides overrides) {
-    DynamicItem<ReplicationConfigOverrides> dynamicItemMock = mock(DynamicItem.class);
-    when(dynamicItemMock.get()).thenReturn(overrides);
-
-    return new ReplicationRemotesApiImpl(
-        secureStoreMock, Providers.of(baseConfig), dynamicItemMock);
+  private ReplicationRemotesApi getReplicationRemotesApi() {
+    return testInjector.getInstance(ReplicationRemotesApi.class);
   }
 
   static class TestReplicationConfigOverrides implements ReplicationConfigOverrides {