diff options
author | Jukka Jokiniva <jukka.jokiniva@qt.io> | 2019-05-03 11:51:55 +0300 |
---|---|---|
committer | Jukka Jokiniva <jukka.jokiniva@qt.io> | 2019-05-10 07:13:26 +0000 |
commit | ef970bc31996f781070f7079c4fd731993101bb6 (patch) | |
tree | e0831074133975c5c9b1dc8892cdea61c536fb05 | |
parent | dd41d9e7b8b9e103f4560724107c27866f9337f3 (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>
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(); + } +} |