Inherit remote replication events from a common parent

The replication plugin models replication events that are directed to a
specific remote.

These are:

RefReplicatedEvent and ReplicationScheduledEvent

These events share many fields (such as project, ref, target) and this
makes sense, as they all represent the status of a ref replication to a
particular remote.

However, rather than inheriting from a common parent, most fields are
copy & pasted redundantly into each event.

Extract common fields by having RefReplicatedEvent and
ReplicationScheduledEvent inherit from a common parent.

This also opens the door for other ref-replication-based events (such as
pull-replication for example) to adhere to the same interface.

Bug: Issue 14819
Change-Id: I29da700bf6671159bd6ae29b00ef6b962ef0fcb5
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/events/RefReplicatedEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/events/RefReplicatedEvent.java
index b0c554e..c8347d1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/events/RefReplicatedEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/events/RefReplicatedEvent.java
@@ -16,22 +16,16 @@
 
 import static com.googlesource.gerrit.plugins.replication.PushResultProcessing.resolveNodeName;
 
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.events.RefEvent;
 import com.googlesource.gerrit.plugins.replication.ReplicationState.RefPushResult;
 import java.util.Objects;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 import org.eclipse.jgit.transport.URIish;
 
-public class RefReplicatedEvent extends RefEvent {
+public class RefReplicatedEvent extends RemoteRefReplicationEvent {
   public static final String TYPE = "ref-replicated";
 
-  public final String project;
-  public final String ref;
   @Deprecated public final String targetNode;
-  public final String targetUri;
-  public final String status;
   public final Status refStatus;
 
   public RefReplicatedEvent(
@@ -40,23 +34,9 @@
       URIish targetUri,
       RefPushResult status,
       RemoteRefUpdate.Status refStatus) {
-    super(TYPE);
-    this.project = project;
-    this.ref = ref;
+    super(TYPE, project, ref, targetUri, status.toString());
     this.targetNode = resolveNodeName(targetUri);
-    this.status = status.toString();
     this.refStatus = refStatus;
-    this.targetUri = targetUri.toASCIIString();
-  }
-
-  @Override
-  public Project.NameKey getProjectNameKey() {
-    return Project.nameKey(project);
-  }
-
-  @Override
-  public String getRefName() {
-    return ref;
   }
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/events/RemoteRefReplicationEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/events/RemoteRefReplicationEvent.java
new file mode 100644
index 0000000..3950c3f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/events/RemoteRefReplicationEvent.java
@@ -0,0 +1,47 @@
+// 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.events;
+
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.events.RefEvent;
+import org.eclipse.jgit.transport.URIish;
+
+public class RemoteRefReplicationEvent extends RefEvent {
+
+  public final String project;
+  public final String ref;
+  public final String status;
+  public final String targetUri;
+
+  public RemoteRefReplicationEvent(
+      String type, String project, String ref, URIish targetUri, @Nullable String status) {
+    super(type);
+    this.project = project;
+    this.ref = ref;
+    this.status = status;
+    this.targetUri = targetUri.toASCIIString();
+  }
+
+  @Override
+  public Project.NameKey getProjectNameKey() {
+    return Project.nameKey(project);
+  }
+
+  @Override
+  public String getRefName() {
+    return ref;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/events/ReplicationScheduledEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/events/ReplicationScheduledEvent.java
index 4a1ade8..a952777 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/events/ReplicationScheduledEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/events/ReplicationScheduledEvent.java
@@ -17,23 +17,16 @@
 import static com.googlesource.gerrit.plugins.replication.PushResultProcessing.resolveNodeName;
 
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.events.RefEvent;
 import org.eclipse.jgit.transport.URIish;
 
-public class ReplicationScheduledEvent extends RefEvent {
+public class ReplicationScheduledEvent extends RemoteRefReplicationEvent {
   public static final String TYPE = "ref-replication-scheduled";
 
-  public final String project;
-  public final String ref;
   @Deprecated public final String targetNode;
-  public final String targetUri;
 
   public ReplicationScheduledEvent(String project, String ref, URIish targetUri) {
-    super(TYPE);
-    this.project = project;
-    this.ref = ref;
+    super(TYPE, project, ref, targetUri, null);
     this.targetNode = resolveNodeName(targetUri);
-    this.targetUri = targetUri.toASCIIString();
   }
 
   @Override
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java
index 2d69a47..4cd55f9 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.replication;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import com.google.common.base.Objects;
 import com.google.gerrit.acceptance.PushOneCommit.Result;
@@ -42,11 +43,13 @@
 import com.googlesource.gerrit.plugins.replication.events.RefReplicatedEvent;
 import com.googlesource.gerrit.plugins.replication.events.RefReplicationDoneEvent;
 import com.googlesource.gerrit.plugins.replication.events.ReplicationScheduledEvent;
+import java.net.URISyntaxException;
 import java.time.Duration;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.URIish;
 import org.junit.Before;
 import org.junit.Test;
@@ -308,6 +311,34 @@
     assertThat(origEvent).isEqualTo(gotEvent);
   }
 
+  @Test
+  public void shouldSerializeRefReplicatedEvent() throws URISyntaxException {
+    RefReplicatedEvent origEvent =
+        new RefReplicatedEvent(
+            project.get(),
+            "refs/heads/master",
+            new URIish(String.format("git://someHost/%s.git", project.get())),
+            ReplicationState.RefPushResult.SUCCEEDED,
+            RemoteRefUpdate.Status.OK);
+
+    assertThat(origEvent)
+        .isEqualTo(eventGson.fromJson(eventGson.toJson(origEvent), RefReplicatedEvent.class));
+  }
+
+  @Test
+  public void shouldSerializeReplicationScheduledEvent() throws URISyntaxException {
+    ReplicationScheduledEvent origEvent =
+        new ReplicationScheduledEvent(
+            project.get(),
+            "refs/heads/master",
+            new URIish(String.format("git://someHost/%s.git", project.get())));
+
+    assertTrue(
+        equals(
+            origEvent,
+            eventGson.fromJson(eventGson.toJson(origEvent), ReplicationScheduledEvent.class)));
+  }
+
   private <T extends RefEvent> void waitForRefEvent(Supplier<List<T>> events, String refName)
       throws InterruptedException {
     WaitUtil.waitUntil(
@@ -352,4 +383,24 @@
       return Objects.hashCode(event);
     }
   }
+
+  private boolean equals(ReplicationScheduledEvent scheduledEvent, Object other) {
+    if (!(other instanceof ReplicationScheduledEvent)) {
+      return false;
+    }
+    ReplicationScheduledEvent event = (ReplicationScheduledEvent) other;
+    if (!Objects.equal(event.project, scheduledEvent.project)) {
+      return false;
+    }
+    if (!Objects.equal(event.ref, scheduledEvent.ref)) {
+      return false;
+    }
+    if (!Objects.equal(event.targetUri, scheduledEvent.targetUri)) {
+      return false;
+    }
+    if (!Objects.equal(event.status, scheduledEvent.status)) {
+      return false;
+    }
+    return Objects.equal(event.targetNode, scheduledEvent.targetNode);
+  }
 }