Propagate FetchRefReplicatedEvent
When a ref is replicated through a REST-API call, invoke
the FetchRefReplicatedEvent synchronously, exactly as the replication
plugin does.
Bug: Issue 13893
Change-Id: I1959583b66e17f47df1cd972da51241c39327899
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommand.java
index 4de9523..ac7e79b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommand.java
@@ -18,11 +18,18 @@
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.collect.ImmutableSet;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.replication.pull.ApplyObjectMetrics;
+import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent;
import com.googlesource.gerrit.plugins.replication.pull.PullReplicationStateLogger;
+import com.googlesource.gerrit.plugins.replication.pull.ReplicationState;
+import com.googlesource.gerrit.plugins.replication.pull.ReplicationState.RefFetchResult;
import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionData;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.MissingParentObjectException;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.RefUpdateException;
@@ -35,6 +42,8 @@
public class ApplyObjectCommand {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private static final Set<RefUpdate.Result> SUCCESSFUL_RESULTS =
ImmutableSet.of(
RefUpdate.Result.NEW,
@@ -45,15 +54,18 @@
private final PullReplicationStateLogger fetchStateLog;
private final ApplyObject applyObject;
private final ApplyObjectMetrics metrics;
+ private final DynamicItem<EventDispatcher> eventDispatcher;
@Inject
public ApplyObjectCommand(
PullReplicationStateLogger fetchStateLog,
ApplyObject applyObject,
- ApplyObjectMetrics metrics) {
+ ApplyObjectMetrics metrics,
+ DynamicItem<EventDispatcher> eventDispatcher) {
this.fetchStateLog = fetchStateLog;
this.applyObject = applyObject;
this.metrics = metrics;
+ this.eventDispatcher = eventDispatcher;
}
public void applyObject(
@@ -65,6 +77,21 @@
RefUpdateState refUpdateState = applyObject.apply(name, new RefSpec(refName), revisionData);
long elapsed = NANOSECONDS.toMillis(context.stop());
+ try {
+ eventDispatcher
+ .get()
+ .postEvent(
+ new FetchRefReplicatedEvent(
+ name.get(),
+ refName,
+ sourceLabel,
+ getStatus(refUpdateState),
+ refUpdateState.getResult()));
+ } catch (PermissionBackendException e) {
+ logger.atSevere().withCause(e).log(
+ "Cannot post event for ref '%s', project %s", refName, name);
+ }
+
if (!isSuccessful(refUpdateState.getResult())) {
String message =
String.format(
@@ -81,6 +108,12 @@
elapsed);
}
+ private RefFetchResult getStatus(RefUpdateState refUpdateState) {
+ return isSuccessful(refUpdateState.getResult())
+ ? ReplicationState.RefFetchResult.SUCCEEDED
+ : ReplicationState.RefFetchResult.FAILED;
+ }
+
private Boolean isSuccessful(RefUpdate.Result result) {
return SUCCESSFUL_RESULTS.contains(result);
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommandTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommandTest.java
new file mode 100644
index 0000000..51051c0
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectCommandTest.java
@@ -0,0 +1,100 @@
+// Copyright (C) 2021 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.googlesource.gerrit.plugins.replication.pull.api;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Lists;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.googlesource.gerrit.plugins.replication.pull.ApplyObjectMetrics;
+import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent;
+import com.googlesource.gerrit.plugins.replication.pull.PullReplicationStateLogger;
+import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionData;
+import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionObjectData;
+import com.googlesource.gerrit.plugins.replication.pull.api.exception.MissingParentObjectException;
+import com.googlesource.gerrit.plugins.replication.pull.api.exception.RefUpdateException;
+import com.googlesource.gerrit.plugins.replication.pull.fetch.ApplyObject;
+import com.googlesource.gerrit.plugins.replication.pull.fetch.RefUpdateState;
+import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ApplyObjectCommandTest {
+ private static final String TEST_SOURCE_LABEL = "test-source-label";
+ private static final String TEST_REF_NAME = "refs/changes/01/1/1";
+ private static final NameKey TEST_PROJECT_NAME = Project.nameKey("test-project");
+ private static final String TEST_REMOTE_NAME = "test-remote-name";
+
+ @Mock private PullReplicationStateLogger fetchStateLog;
+ @Mock private ApplyObject applyObject;
+ @Mock private ApplyObjectMetrics metrics;
+ @Mock private DynamicItem<EventDispatcher> eventDispatcherDataItem;
+ @Mock private EventDispatcher eventDispatcher;
+ @Mock private Timer1.Context<String> timetContext;
+ @Captor ArgumentCaptor<Event> eventCaptor;
+
+ private ApplyObjectCommand objectUnderTest;
+
+ @Before
+ public void setup() throws MissingParentObjectException, IOException {
+ RefUpdateState state = new RefUpdateState(TEST_REMOTE_NAME, RefUpdate.Result.NEW);
+ when(eventDispatcherDataItem.get()).thenReturn(eventDispatcher);
+ when(metrics.start(anyString())).thenReturn(timetContext);
+ when(timetContext.stop()).thenReturn(100L);
+ when(applyObject.apply(any(), any(), any())).thenReturn(state);
+
+ objectUnderTest =
+ new ApplyObjectCommand(fetchStateLog, applyObject, metrics, eventDispatcherDataItem);
+ }
+
+ @Test
+ public void shouldSendEventWhenApplyObject()
+ throws PermissionBackendException, IOException, RefUpdateException,
+ MissingParentObjectException {
+ objectUnderTest.applyObject(
+ TEST_PROJECT_NAME, TEST_REF_NAME, createSampleRevisionData(), TEST_SOURCE_LABEL);
+
+ verify(eventDispatcher).postEvent(eventCaptor.capture());
+ Event sentEvent = eventCaptor.getValue();
+ assertThat(sentEvent).isInstanceOf(FetchRefReplicatedEvent.class);
+ FetchRefReplicatedEvent fetchEvent = (FetchRefReplicatedEvent) sentEvent;
+ assertThat(fetchEvent.getProjectNameKey()).isEqualTo(TEST_PROJECT_NAME);
+ assertThat(fetchEvent.getRefName()).isEqualTo(TEST_REF_NAME);
+ }
+
+ private RevisionData createSampleRevisionData() {
+ RevisionObjectData commitData = new RevisionObjectData(Constants.OBJ_COMMIT, new byte[] {});
+ RevisionObjectData treeData = new RevisionObjectData(Constants.OBJ_TREE, new byte[] {});
+ return new RevisionData(commitData, treeData, Lists.newArrayList());
+ }
+}