Extract UpdateHeadCommand
Move logic related to HEAD update from the action class to a command in
order to reuse it when HEAD modification event comes from stream events
Change-Id: Id9c9d297bb5e39d944386b9e514b5ac69ec0ea1a
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadAction.java
index 800f2b2..9a3b4fa 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadAction.java
@@ -15,56 +15,31 @@
package com.googlesource.gerrit.plugins.replication.pull.api;
import com.google.common.base.Strings;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.HeadInput;
-import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.server.events.EventDispatcher;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.replication.LocalFS;
-import com.googlesource.gerrit.plugins.replication.pull.Context;
-import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent;
-import com.googlesource.gerrit.plugins.replication.pull.GerritConfigOps;
-import com.googlesource.gerrit.plugins.replication.pull.ReplicationState;
-import java.net.HttpURLConnection;
-import java.util.Optional;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.transport.URIish;
@Singleton
public class UpdateHeadAction implements RestModifyView<ProjectResource, HeadInput> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final GerritConfigOps gerritConfigOps;
private final FetchPreconditions preconditions;
- private final DynamicItem<EventDispatcher> eventDispatcher;
- private static final URIish EMPTY_URI = new URIish();
+ private final UpdateHeadCommand updateHeadCommand;
@Inject
- UpdateHeadAction(
- GerritConfigOps gerritConfigOps,
- FetchPreconditions preconditions,
- DynamicItem<EventDispatcher> eventDispatcher) {
- this.gerritConfigOps = gerritConfigOps;
+ UpdateHeadAction(FetchPreconditions preconditions, UpdateHeadCommand updateHeadCommand) {
this.preconditions = preconditions;
- this.eventDispatcher = eventDispatcher;
+ this.updateHeadCommand = updateHeadCommand;
}
@Override
public Response<?> apply(ProjectResource projectResource, HeadInput input)
throws AuthException, BadRequestException, ResourceConflictException, Exception {
- Response<String> res = null;
-
if (input == null || Strings.isNullOrEmpty(input.ref)) {
throw new BadRequestException("ref required");
}
@@ -74,50 +49,8 @@
throw new AuthException("Update head not permitted");
}
- // TODO: the .git suffix should not be added here, but rather it should be
- // dealt with by the caller, honouring the naming style from the
- // replication.config (Issue 15221)
- Optional<URIish> maybeRepo =
- gerritConfigOps.getGitRepositoryURI(String.format("%s.git", projectResource.getName()));
+ updateHeadCommand.doUpdate(projectResource.getNameKey(), ref);
- try {
- if (maybeRepo.isPresent()) {
- if (new LocalFS(maybeRepo.get()).updateHead(projectResource.getNameKey(), ref)) {
- return res = Response.ok(ref);
- }
- throw new UnprocessableEntityException(
- String.format(
- "Could not update HEAD of repo %s to ref %s", projectResource.getName(), ref));
- }
- } finally {
- fireEvent(
- projectResource.getNameKey(),
- res != null && res.statusCode() == HttpURLConnection.HTTP_OK);
- }
- throw new ResourceNotFoundException(
- String.format("Could not compute URL for repo: %s", projectResource.getName()));
- }
-
- private void fireEvent(Project.NameKey projectName, boolean succeeded) {
- try {
- Context.setLocalEvent(true);
- eventDispatcher
- .get()
- .postEvent(
- new FetchRefReplicatedEvent(
- projectName.get(),
- RefNames.HEAD,
- EMPTY_URI, // TODO: the remote label is not passed as parameter, hence cannot be
- // propagated to the event
- succeeded
- ? ReplicationState.RefFetchResult.SUCCEEDED
- : ReplicationState.RefFetchResult.FAILED,
- RefUpdate.Result.FORCED));
- } catch (PermissionBackendException e) {
- logger.atSevere().withCause(e).log(
- "Cannot post event for refs/meta/config on project %s", projectName);
- } finally {
- Context.unsetLocalEvent();
- }
+ return Response.ok(ref);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadCommand.java
new file mode 100644
index 0000000..53bf5d9
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/UpdateHeadCommand.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 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 com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.replication.LocalFS;
+import com.googlesource.gerrit.plugins.replication.pull.Context;
+import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent;
+import com.googlesource.gerrit.plugins.replication.pull.GerritConfigOps;
+import com.googlesource.gerrit.plugins.replication.pull.ReplicationState;
+import java.util.Optional;
+import javax.inject.Inject;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.transport.URIish;
+
+@Singleton
+public class UpdateHeadCommand {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final URIish EMPTY_URI = new URIish();
+
+ private final GerritConfigOps gerritConfigOps;
+ private final DynamicItem<EventDispatcher> eventDispatcher;
+
+ @Inject
+ UpdateHeadCommand(GerritConfigOps gerritConfigOps, DynamicItem<EventDispatcher> eventDispatcher) {
+ this.gerritConfigOps = gerritConfigOps;
+ this.eventDispatcher = eventDispatcher;
+ }
+
+ public void doUpdate(Project.NameKey project, String ref)
+ throws UnprocessableEntityException, ResourceNotFoundException {
+ boolean succeeded = false;
+ // TODO: the .git suffix should not be added here, but rather it should be
+ // dealt with by the caller, honouring the naming style from the
+ // replication.config (Issue 15221)
+ Optional<URIish> maybeRepo =
+ gerritConfigOps.getGitRepositoryURI(String.format("%s.git", project.get()));
+
+ logger.atInfo().log("do update: %s %s", project, ref);
+ try {
+ if (maybeRepo.isPresent()) {
+ if (new LocalFS(maybeRepo.get()).updateHead(project, ref)) {
+ succeeded = true;
+ return;
+ }
+
+ throw new UnprocessableEntityException(
+ String.format("Could not update HEAD of repo %s to ref %s", project, ref));
+ }
+ } finally {
+ fireEvent(project, succeeded);
+ }
+ throw new ResourceNotFoundException(
+ String.format("Could not compute URL for repo: %s", project.get()));
+ }
+
+ private void fireEvent(Project.NameKey projectName, boolean succeeded) {
+ try {
+ Context.setLocalEvent(true);
+ eventDispatcher
+ .get()
+ .postEvent(
+ new FetchRefReplicatedEvent(
+ projectName.get(),
+ RefNames.HEAD,
+ EMPTY_URI, // TODO: the remote label is not passed as parameter, hence cannot be
+ // propagated to the event
+ succeeded
+ ? ReplicationState.RefFetchResult.SUCCEEDED
+ : ReplicationState.RefFetchResult.FAILED,
+ RefUpdate.Result.FORCED));
+ } catch (PermissionBackendException e) {
+ logger.atSevere().withCause(e).log(
+ "Cannot post event for refs/meta/config on project %s", projectName);
+ } finally {
+ Context.unsetLocalEvent();
+ }
+ }
+}