aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJukka Jokiniva <jukka.jokiniva@qt.io>2019-05-03 11:51:55 +0300
committerJukka Jokiniva <jukka.jokiniva@qt.io>2019-05-10 07:13:26 +0000
commitef970bc31996f781070f7079c4fd731993101bb6 (patch)
treee0831074133975c5c9b1dc8892cdea61c536fb05
parentdd41d9e7b8b9e103f4560724107c27866f9337f3 (diff)
Add new admin only API to modify a change status
Fixes: QTBI-1658 Change-Id: I4dc8a206841cdcb6bd281ec13b4f8d8708554239 Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
-rw-r--r--src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatus.java127
-rw-r--r--src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java1
-rw-r--r--src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatusIT.java151
3 files changed, 279 insertions, 0 deletions
diff --git a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatus.java b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatus.java
new file mode 100644
index 0000000..255e14e
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatus.java
@@ -0,0 +1,127 @@
+//
+// Copyright (C) 2019 The Qt Company
+//
+
+package com.googlesource.gerrit.plugins.qtcodereview;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.gerrit.sshd.SshCommand;
+import com.google.gerrit.sshd.CommandMetaData;
+import com.google.gwtorm.server.OrmException;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.lang.NumberFormatException;
+import java.util.List;
+
+import org.kohsuke.args4j.Option;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+@CommandMetaData(name="change-status", description="Modify change status for admins. NOTE: This only affects change attribure. No real git merge or revert will be performed.")
+class QtCommandAdminChangeStatus extends SshCommand {
+
+ @Inject
+ private Provider<ReviewDb> dbProvider;
+
+ @Inject
+ Provider<InternalChangeQuery> queryProvider;
+
+ @Inject
+ private BatchUpdate.Factory updateFactory;
+
+ @Inject
+ private QtChangeUpdateOp.Factory qtUpdateFactory;
+
+ @Option(name = "--project", aliases = {"-p"},
+ required = true, usage = "project name")
+ private String project;
+
+ @Option(name = "--change-id", aliases = {"-c"},
+ required = true, usage = "change id (numeric, like in web url)")
+ private String changeId;
+
+ @Option(name = "--from",
+ required = true, usage = "new, staged, integrating, merged, abandoned, deferred")
+ private String fromStr;
+
+ @Option(name = "--to",
+ required = true, usage = "new, staged, integrating, merged, abandoned, deferred")
+ private String toStr;
+
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Override
+ protected void run() throws UnloggedFailure {
+ logger.atInfo().log("qtcodereview: admin change-status start " + changeId);
+
+ try {
+ Change.Status to = toStatus(toStr);
+ if (to == null) throw die("invalid to status");
+
+ Change.Status from = toStatus(fromStr);
+ if (from == null) throw die("invalid from status");
+
+ Change.Id id = Change.Id.parse(changeId);
+ if (id.get() == 0) throw die("invalid change-id");
+
+ InternalChangeQuery query = queryProvider.get();
+ List<ChangeData> list = query.byLegacyChangeId(id);
+ if (list.isEmpty()) throw die("change not found");
+ if (list.size() > 1) throw die("multiple changes found");
+ ChangeData change = list.get(0);
+
+ Project.NameKey projectKey = QtUtil.getProjectKey(project);
+ QtChangeUpdateOp op = qtUpdateFactory.create(to, null, null, null, null);
+ try (BatchUpdate u = updateFactory.create(dbProvider.get(), projectKey, user, TimeUtil.nowTs())) {
+ Change c = change.change();
+ if (c.getStatus() == from) {
+ u.addOp(c.getId(), op);
+ u.execute();
+ } else {
+ throw die("change status was not " + fromStr);
+ }
+ }
+ logger.atInfo().log("qtcodereview: admin change-status done");
+ } catch(NumberFormatException e) {
+ throw die("change-id not numeric");
+ } catch(OrmException e) {
+ logger.atSevere().log("qtcodereview: change-status error %s", e.getMessage());
+ throw die("Database query failed");
+ } catch (UpdateException | RestApiException e) {
+ logger.atSevere().log("qtcodereview: change-status error %s", e.getMessage());
+ throw die("Database update failed");
+ }
+ }
+
+ private Change.Status toStatus(String str) {
+ switch (str) {
+ case "new":
+ return Change.Status.NEW;
+ case "staged":
+ return Change.Status.STAGED;
+ case "integrating":
+ return Change.Status.INTEGRATING;
+ case "merged":
+ return Change.Status.MERGED;
+ case "abandoned":
+ return Change.Status.ABANDONED;
+ case "deferred":
+ return Change.Status.DEFERRED;
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java
index 306a5cb..bc97492 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java
@@ -11,6 +11,7 @@ class QtSshModule extends PluginCommandModule {
@Override
protected void configureCommands() {
command(QtCommandPing.class);
+ command(QtCommandAdminChangeStatus.class);
command(QtCommandBuildApprove.class);
command(QtCommandNewBuild.class);
command(QtCommandListStaging.class);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatusIT.java b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatusIT.java
new file mode 100644
index 0000000..4a29127
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandAdminChangeStatusIT.java
@@ -0,0 +1,151 @@
+// Copyright (C) 2019 The Qt Company
+
+package com.googlesource.gerrit.plugins.qtcodereview;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
+
+import org.junit.Test;
+
+@TestPlugin(
+ name = "gerrit-plugin-qt-workflow",
+ sysModule = "com.googlesource.gerrit.plugins.qtcodereview.QtModule",
+ sshModule = "com.googlesource.gerrit.plugins.qtcodereview.QtSshModule"
+)
+
+@UseSsh
+public class QtCommandAdminChangeStatusIT extends QtCodeReviewIT {
+
+ @Test
+ public void New_Staged_Integrating_Merged_Abandoned_Deferred_New() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+
+ String changeId = Integer.toString(c.getChange().getId().get());
+ qtAdminChangeStatus(c, changeId, "new", "staged", Change.Status.STAGED);
+ qtAdminChangeStatus(c, changeId, "staged", "integrating", Change.Status.INTEGRATING);
+ qtAdminChangeStatus(c, changeId, "integrating", "merged", Change.Status.MERGED);
+ qtAdminChangeStatus(c, changeId, "merged", "abandoned", Change.Status.ABANDONED);
+ qtAdminChangeStatus(c, changeId, "abandoned", "deferred", Change.Status.DEFERRED);
+ qtAdminChangeStatus(c, changeId, "deferred", "new", Change.Status.NEW);
+ }
+
+ @Test
+ public void error_ChangeNotFound() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ String result;
+ result = qtAdminChangeStatusExpectFail(c, "9999", "new", "staged", Change.Status.NEW);
+ assertThat(result).contains("change not found");
+ }
+
+ @Test
+ public void error_InvalidChangeId() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ String result;
+ result = qtAdminChangeStatusExpectFail(c, "invalid", "new", "staged", Change.Status.NEW);
+ assertThat(result).contains("change-id not numeric");
+ }
+
+ @Test
+ public void error_Invalid_From_Status() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+
+ String result;
+ String changeId = Integer.toString(c.getChange().getId().get());
+ result = qtAdminChangeStatusExpectFail(c, changeId, "invalid", "staged", Change.Status.NEW);
+ assertThat(result).contains("invalid from status");
+ }
+
+ @Test
+ public void error_Invalid_To_Status() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+
+ String result;
+ String changeId = Integer.toString(c.getChange().getId().get());
+ result = qtAdminChangeStatusExpectFail(c, changeId, "new", "staaaaged", Change.Status.NEW);
+ assertThat(result).contains("invalid to status");
+ }
+
+ @Test
+ public void error_Wrong_From_Status() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+
+ String result;
+ String changeId = Integer.toString(c.getChange().getId().get());
+ result = qtAdminChangeStatusExpectFail(c, changeId, "merged", "staged", Change.Status.NEW);
+ assertThat(result).contains("change status was not");
+ }
+ @Test
+ public void error_NoPermission() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ String changeId = Integer.toString(c.getChange().getId().get());
+
+ String commandStr;
+ commandStr ="gerrit-plugin-qt-workflow change-status";
+ commandStr += " --project " + project.get();
+ commandStr += " --change-id " + changeId;
+ commandStr += " --from new";
+ commandStr += " --to merged";
+ String resultStr = userSshSession.exec(commandStr);
+ assertThat(userSshSession.getError()).contains("not permitted");
+
+ Change change = c.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.NEW);
+ }
+
+ private void qtAdminChangeStatus(PushOneCommit.Result c,
+ String changeId,
+ String from,
+ String to,
+ Change.Status expectedStatus)
+ throws Exception {
+ String result = qtAdminChangeStatusCommon(c,
+ changeId,
+ from,
+ to,
+ expectedStatus,
+ false);
+ assertThat(result).isNull();
+ }
+
+ private String qtAdminChangeStatusExpectFail(PushOneCommit.Result c,
+ String changeId,
+ String from,
+ String to,
+ Change.Status expectedStatus)
+ throws Exception {
+ return qtAdminChangeStatusCommon(c,
+ changeId,
+ from,
+ to,
+ expectedStatus,
+ true);
+ }
+
+
+ private String qtAdminChangeStatusCommon(PushOneCommit.Result c,
+ String changeId,
+ String from,
+ String to,
+ Change.Status expectedStatus,
+ Boolean expectFail)
+ throws Exception {
+ String commandStr;
+ commandStr ="gerrit-plugin-qt-workflow change-status";
+ commandStr += " --project " + project.get();
+ commandStr += " --change-id " + changeId;
+ commandStr += " --from " + from;
+ commandStr += " --to " + to;
+ String resultStr = adminSshSession.exec(commandStr);
+ if (expectFail) assertThat(adminSshSession.getError()).isNotNull();
+ else assertThat(adminSshSession.getError()).isNull();
+
+ Change change = c.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(expectedStatus);
+ return adminSshSession.getError();
+ }
+}