Use refName instead of objectId in the fetch REST Api

When requesting a remote fetch, specify the ref-name to be fetched
instead of the SHA1 of the ref.

Fetching by SHA1 has two issues:
- The remote Git server may disallow exposing the fetch by SHA1
- Fetching the SHA1 does not update the local ref

Change-Id: I52fba1166b0d5df3a498af2702328cc7723b9a41
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java
index ce02d56..736fbf0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java
@@ -51,7 +51,7 @@
 
   public static class Input {
     public String label;
-    public String objectId;
+    public String refName;
     public boolean async;
   }
 
@@ -62,8 +62,8 @@
         throw new BadRequestException("Source label cannot be null or empty");
       }
 
-      if (Strings.isNullOrEmpty(input.objectId)) {
-        throw new BadRequestException("Ref-update objectId cannot be null or empty");
+      if (Strings.isNullOrEmpty(input.refName)) {
+        throw new BadRequestException("Ref-update refname cannot be null or empty");
       }
 
       if (input.async) {
@@ -83,7 +83,7 @@
   private Response<?> applySync(Project.NameKey project, Input input)
       throws InterruptedException, ExecutionException, RemoteConfigurationMissingException,
           TimeoutException {
-    command.fetch(project, input.label, input.objectId);
+    command.fetch(project, input.label, input.refName);
     return Response.created(input);
   }
 
@@ -117,16 +117,16 @@
     @Override
     public void run() {
       try {
-        command.fetch(project, input.label, input.objectId);
+        command.fetch(project, input.label, input.refName);
       } catch (InterruptedException
           | ExecutionException
           | RemoteConfigurationMissingException
           | TimeoutException e) {
         log.atSevere().withCause(e).log(
-            "Exception during the async fetch call for project {}, label {} and object id {}",
+            "Exception during the async fetch call for project {}, label {} and ref name {}",
             project.get(),
             input.label,
-            input.objectId);
+            input.refName);
       }
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java
index 749adc2..b3ab83d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java
@@ -45,7 +45,7 @@
     this.sources = sources;
   }
 
-  public void fetch(Project.NameKey name, String label, String objectId)
+  public void fetch(Project.NameKey name, String label, String refName)
       throws InterruptedException, ExecutionException, RemoteConfigurationMissingException,
           TimeoutException {
     ReplicationState state =
@@ -59,7 +59,7 @@
     }
 
     try {
-      Future<?> future = source.get().schedule(name, objectId, state, true);
+      Future<?> future = source.get().schedule(name, refName, state, true);
       state.markAllFetchTasksScheduled();
       future.get(source.get().getTimeout(), TimeUnit.SECONDS);
     } catch (ExecutionException
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchActionTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchActionTest.java
index 045b882..39c6717 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchActionTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchActionTest.java
@@ -49,7 +49,7 @@
   FetchAction fetchAction;
   String label = "instance-2-label";
   String url = "file:///gerrit-host/instance-1/git/${name}.git";
-  String objectId = "c90989ed7a8ab01f1bdd022872428f020b866358";
+  String refName = "refs/heads/master";
   String location = "http://gerrit-host/a/config/server/tasks/08d173e9";
   int taskId = 1234;
 
@@ -83,7 +83,7 @@
   public void shouldReturnCreatedResponseCode() throws RestApiException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     Response<?> response = fetchAction.apply(projectResource, inputParams);
 
@@ -92,10 +92,10 @@
 
   @SuppressWarnings("cast")
   @Test
-  public void shouldReturnSourceUrlAndObjectIdAsAResponseBody() throws Exception {
+  public void shouldReturnSourceUrlAndrefNameAsAResponseBody() throws Exception {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     Response<?> response = fetchAction.apply(projectResource, inputParams);
 
@@ -105,7 +105,7 @@
   @Test(expected = BadRequestException.class)
   public void shouldThrowBadRequestExceptionWhenMissingLabel() throws Exception {
     FetchAction.Input inputParams = new FetchAction.Input();
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     fetchAction.apply(projectResource, inputParams);
   }
@@ -114,13 +114,13 @@
   public void shouldThrowBadRequestExceptionWhenEmptyLabel() throws Exception {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = "";
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     fetchAction.apply(projectResource, inputParams);
   }
 
   @Test(expected = BadRequestException.class)
-  public void shouldThrowBadRequestExceptionWhenMissingObjectId() throws Exception {
+  public void shouldThrowBadRequestExceptionWhenMissingrefName() throws Exception {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
 
@@ -128,10 +128,10 @@
   }
 
   @Test(expected = BadRequestException.class)
-  public void shouldThrowBadRequestExceptionWhenEmptyObjectId() throws Exception {
+  public void shouldThrowBadRequestExceptionWhenEmptyrefName() throws Exception {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = "";
+    inputParams.refName = "";
 
     fetchAction.apply(projectResource, inputParams);
   }
@@ -142,7 +142,7 @@
           RemoteConfigurationMissingException, TimeoutException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     doThrow(new InterruptedException()).when(fetchCommand).fetch(any(), any(), any());
 
@@ -155,7 +155,7 @@
           RemoteConfigurationMissingException, TimeoutException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = "non-existing-label";
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     doThrow(new RemoteConfigurationMissingException(""))
         .when(fetchCommand)
@@ -170,7 +170,7 @@
           RemoteConfigurationMissingException, TimeoutException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     doThrow(new ExecutionException(new RuntimeException()))
         .when(fetchCommand)
@@ -185,7 +185,7 @@
           RemoteConfigurationMissingException, TimeoutException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     doThrow(new IllegalStateException()).when(fetchCommand).fetch(any(), any(), any());
 
@@ -198,7 +198,7 @@
           RemoteConfigurationMissingException, TimeoutException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
 
     doThrow(new TimeoutException()).when(fetchCommand).fetch(any(), any(), any());
 
@@ -209,7 +209,7 @@
   public void shouldReturnScheduledTaskForAsyncCall() throws RestApiException {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
     inputParams.async = true;
 
     Response<?> response = fetchAction.apply(projectResource, inputParams);
@@ -220,7 +220,7 @@
   public void shouldLocationHeaderForAsyncCall() throws Exception {
     FetchAction.Input inputParams = new FetchAction.Input();
     inputParams.label = label;
-    inputParams.objectId = objectId;
+    inputParams.refName = refName;
     inputParams.async = true;
 
     Response<?> response = fetchAction.apply(projectResource, inputParams);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommandTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommandTest.java
index 7536caf..e625e0f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommandTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommandTest.java
@@ -45,7 +45,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 public class FetchCommandTest {
-  private static final String OBJECT_ID_TO_FETCH = "8f22c45da5ce298291b7329552568aae1bb62c10";
+  private static final String REF_NAME_TO_FETCH = "refs/heads/master";
   @Mock ReplicationState state;
   @Mock ReplicationState.Factory fetchReplicationStateFactory;
   @Mock PullReplicationStateLogger fetchStateLog;
@@ -71,27 +71,27 @@
     when(fetchReplicationStateFactory.create(any())).thenReturn(state);
     when(source.getRemoteConfigName()).thenReturn(label);
     when(sources.getAll()).thenReturn(Lists.newArrayList(source));
-    when(source.schedule(projectName, OBJECT_ID_TO_FETCH, state, true))
+    when(source.schedule(projectName, REF_NAME_TO_FETCH, state, true))
         .thenReturn(CompletableFuture.completedFuture(null));
     objectUnderTest = new FetchCommand(fetchReplicationStateFactory, fetchStateLog, sources);
   }
 
   @Test
-  public void shouldFetchRefUpdate()
+  public void shouldScheduleRefFetch()
       throws InterruptedException, ExecutionException, RemoteConfigurationMissingException,
           TimeoutException {
-    objectUnderTest.fetch(projectName, label, OBJECT_ID_TO_FETCH);
+    objectUnderTest.fetch(projectName, label, REF_NAME_TO_FETCH);
 
-    verify(source, times(1)).schedule(projectName, OBJECT_ID_TO_FETCH, state, true);
+    verify(source, times(1)).schedule(projectName, REF_NAME_TO_FETCH, state, true);
   }
 
   @Test
   public void shouldMarkAllFetchTasksScheduled()
       throws InterruptedException, ExecutionException, RemoteConfigurationMissingException,
           TimeoutException {
-    objectUnderTest.fetch(projectName, label, OBJECT_ID_TO_FETCH);
+    objectUnderTest.fetch(projectName, label, REF_NAME_TO_FETCH);
 
-    verify(source, times(1)).schedule(projectName, OBJECT_ID_TO_FETCH, state, true);
+    verify(source, times(1)).schedule(projectName, REF_NAME_TO_FETCH, state, true);
     verify(state, times(1)).markAllFetchTasksScheduled();
   }
 
@@ -99,7 +99,7 @@
   public void shouldUpdateStateWhenRemoteConfigNameIsMissing() {
     assertThrows(
         RemoteConfigurationMissingException.class,
-        () -> objectUnderTest.fetch(projectName, "unknownLabel", OBJECT_ID_TO_FETCH));
+        () -> objectUnderTest.fetch(projectName, "unknownLabel", REF_NAME_TO_FETCH));
     verify(fetchStateLog, times(1)).error(anyString(), eq(state));
   }
 
@@ -108,12 +108,12 @@
   public void shouldUpdateStateWhenInterruptedException()
       throws InterruptedException, ExecutionException, TimeoutException {
     when(future.get(anyLong(), eq(TimeUnit.SECONDS))).thenThrow(new InterruptedException());
-    when(source.schedule(projectName, OBJECT_ID_TO_FETCH, state, true)).thenReturn(future);
+    when(source.schedule(projectName, REF_NAME_TO_FETCH, state, true)).thenReturn(future);
 
     InterruptedException e =
         assertThrows(
             InterruptedException.class,
-            () -> objectUnderTest.fetch(projectName, label, OBJECT_ID_TO_FETCH));
+            () -> objectUnderTest.fetch(projectName, label, REF_NAME_TO_FETCH));
     verify(fetchStateLog, times(1)).error(anyString(), eq(e), eq(state));
   }
 
@@ -123,12 +123,12 @@
       throws InterruptedException, ExecutionException, TimeoutException {
     when(future.get(anyLong(), eq(TimeUnit.SECONDS)))
         .thenThrow(new ExecutionException(new Exception()));
-    when(source.schedule(projectName, OBJECT_ID_TO_FETCH, state, true)).thenReturn(future);
+    when(source.schedule(projectName, REF_NAME_TO_FETCH, state, true)).thenReturn(future);
 
     ExecutionException e =
         assertThrows(
             ExecutionException.class,
-            () -> objectUnderTest.fetch(projectName, label, OBJECT_ID_TO_FETCH));
+            () -> objectUnderTest.fetch(projectName, label, REF_NAME_TO_FETCH));
     verify(fetchStateLog, times(1)).error(anyString(), eq(e), eq(state));
   }
 
@@ -137,12 +137,12 @@
   public void shouldUpdateStateWhenTimeoutException()
       throws InterruptedException, ExecutionException, TimeoutException {
     when(future.get(anyLong(), eq(TimeUnit.SECONDS))).thenThrow(new TimeoutException());
-    when(source.schedule(projectName, OBJECT_ID_TO_FETCH, state, true)).thenReturn(future);
+    when(source.schedule(projectName, REF_NAME_TO_FETCH, state, true)).thenReturn(future);
 
     TimeoutException e =
         assertThrows(
             TimeoutException.class,
-            () -> objectUnderTest.fetch(projectName, label, OBJECT_ID_TO_FETCH));
+            () -> objectUnderTest.fetch(projectName, label, REF_NAME_TO_FETCH));
     verify(fetchStateLog, times(1)).error(anyString(), eq(e), eq(state));
   }
 }