Refactor remote API calls to use shared HTTP helper

Introduce a reusable `execute` helper method to streamline HTTP request
handling for remote Gerrit API calls. This refactoring reduces code
duplication and improves maintainability by centralizing request
execution and response parsing logic.

Change-Id: I3b138f707a2de67d71520992d6612d63366b8aaa
diff --git a/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java b/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java
index aff321b..bdeda40 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java
@@ -19,6 +19,8 @@
 import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
 import static com.google.inject.Scopes.SINGLETON;
 
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
@@ -59,7 +61,6 @@
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 import java.io.IOException;
-import java.lang.reflect.Type;
 import java.net.Authenticator;
 import java.net.PasswordAuthentication;
 import java.net.URI;
@@ -367,49 +368,51 @@
           account, cfg.getProjectWatches(), cfg.asCachedPreferences());
     }
 
-    protected AccountDetailInfo getAccountFromRemoteSite(Account.Id accountId) {
+    private <T> T execute(
+        Supplier<HttpRequest> requestSupplier,
+        Function<String, T> responseParser,
+        String errorMessage) {
       rateLimiter.getLimiter().acquire();
-      HttpRequest request =
-          HttpRequest.newBuilder().uri(config.getAccountDetailUri(accountId)).GET().build();
+      HttpRequest request = requestSupplier.get();
+
       try {
         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
         if (response.statusCode() == 200) {
-          Type type = new TypeToken<AccountDetailInfo>() {}.getType();
-          return gson.fromJson(response.body(), type);
+          return responseParser.apply(response.body());
         }
-        logger.atSevere().log(
-            "Failed to fetch account from remote Gerrit %s, %s", accountId, response.body());
+        logger.atSevere().log("%s %s, %s", errorMessage, response.statusCode(), response.body());
       } catch (IOException | InterruptedException e) {
-        logger.atSevere().withCause(e).log("Failed to fetch account from remote Gerrit");
+        logger.atSevere().withCause(e).log("%s", errorMessage);
       }
       return null;
     }
 
+    protected AccountDetailInfo getAccountFromRemoteSite(Account.Id accountId) {
+      return execute(
+          () -> HttpRequest.newBuilder().uri(config.getAccountDetailUri(accountId)).GET().build(),
+          body -> gson.fromJson(body, new TypeToken<AccountDetailInfo>() {}.getType()),
+          String.format("Failed to fetch account %s from remote Gerrit", accountId));
+    }
+
     public List<ExternalId> getExternalIdsFromRemoteSite(Account.Id accountId)
         throws AccountNotFoundInRemoteGerritException {
-      rateLimiter.getLimiter().acquire();
-      HttpRequest request =
-          HttpRequest.newBuilder().uri(config.getExternalIdsUri(accountId)).GET().build();
-
-      try {
-        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
-        if (response.statusCode() == 200) {
-          Type type = new TypeToken<List<AccountExternalIdInfo>>() {}.getType();
-          List<AccountExternalIdInfo> accountExternalIdInfos = gson.fromJson(response.body(), type);
-          return accountExternalIdInfos.stream()
-              .map(
-                  i ->
-                      ExternalId.create(
-                          externalIdKeyFactory.parse(i.identity), accountId, i.emailAddress, null))
-              .collect(Collectors.toList());
-        }
-        logger.atSevere().log(
-            "Failed to fetch remote ids for account %s, %s", accountId, response.body());
-      } catch (IOException | InterruptedException e) {
-        logger.atSevere().withCause(e).log("Failed to fetch remote ids for account %s", accountId);
+      List<AccountExternalIdInfo> accountExternalIdInfos =
+          execute(
+              () -> HttpRequest.newBuilder().uri(config.getExternalIdsUri(accountId)).GET().build(),
+              body ->
+                  gson.fromJson(body, new TypeToken<List<AccountExternalIdInfo>>() {}.getType()),
+              String.format(
+                  "Failed to fetch remote ids for account %s from remote Gerrit", accountId));
+      if (accountExternalIdInfos == null) {
+        throw new AccountNotFoundInRemoteGerritException(
+            String.format("Account %s not found in remote Gerrit", accountId));
       }
-      throw new AccountNotFoundInRemoteGerritException(
-          String.format("%s not found in remote Gerrit", accountId));
+      return accountExternalIdInfos.stream()
+          .map(
+              i ->
+                  ExternalId.create(
+                      externalIdKeyFactory.parse(i.identity), accountId, i.emailAddress, null))
+          .collect(Collectors.toList());
     }
 
     protected HttpClient getHttpClient() {