MergeValidationException: Allow arbitrary string messages

The purpose of these exceptions is to be thrown by merge validation
listeners, which may come from plugins. It doesn't make sense to force
plugins to choose a status message from a list of messages in
CommitMergeStatus that is a hard-coded enum in core.

This restriction on MergeValidationException is how we ended up with a
bunch of very specific error messages tightly coupled to the
ProjectConfigValidator.

Instead, allow arbitrary string messages, which we are now able to
effectively report from MergeOp. We don't even need to set the status
on the CodeReviewCommit when handling this exception from within
MergeOp, since failFast causes the process to abort before we ever
inspect the CodeReviewCommits.

Change-Id: Iaef3e0a5080522b9812e4709217955c877809567
This commit is contained in:
Dave Borowitz 2015-12-22 17:53:19 -05:00
parent b958579cb1
commit 63d6c82515
5 changed files with 38 additions and 63 deletions

View File

@ -65,36 +65,7 @@ public enum CommitMergeStatus {
/** */ /** */
NOT_FAST_FORWARD("Project policy requires all submissions to be a fast-forward.\n" NOT_FAST_FORWARD("Project policy requires all submissions to be a fast-forward.\n"
+ "\n" + "\n"
+ "Please rebase the change locally and upload again for review."), + "Please rebase the change locally and upload again for review.");
/** */
INVALID_PROJECT_CONFIGURATION("Change contains an invalid project configuration."),
/** */
INVALID_PROJECT_CONFIGURATION_PLUGIN_VALUE_NOT_PERMITTED(
"Change contains an invalid project configuration:\n"
+ "One of the plugin configuration parameters has a value that is not permitted."),
/** */
INVALID_PROJECT_CONFIGURATION_PLUGIN_VALUE_NOT_EDITABLE(
"Change contains an invalid project configuration:\n"
+ "One of the plugin configuration parameters is not editable."),
/** */
INVALID_PROJECT_CONFIGURATION_PARENT_PROJECT_NOT_FOUND(
"Change contains an invalid project configuration:\n"
+ "Parent project does not exist."),
/** */
INVALID_PROJECT_CONFIGURATION_ROOT_PROJECT_CANNOT_HAVE_PARENT(
"Change contains an invalid project configuration:\n"
+ "The root project cannot have a parent."),
/** */
SETTING_PARENT_PROJECT_ONLY_ALLOWED_BY_ADMIN(
"Change contains a project configuration that changes the parent project.\n"
+ "The change must be submitted by a Gerrit administrator.");
private String message; private String message;

View File

@ -692,8 +692,7 @@ public class MergeOp implements AutoCloseable {
mergeValidators.validatePreMerge( mergeValidators.validatePreMerge(
or.repo, commit, or.project, destBranch, ps.getId()); or.repo, commit, or.project, destBranch, ps.getId());
} catch (MergeValidationException mve) { } catch (MergeValidationException mve) {
commit.setStatusCode(mve.getStatus()); problems.put(changeId, mve.getMessage());
problems.put(changeId, mve.getStatus().toString());
continue; continue;
} }
@ -881,12 +880,6 @@ public class MergeOp implements AutoCloseable {
case MANUAL_RECURSIVE_MERGE: case MANUAL_RECURSIVE_MERGE:
case CANNOT_CHERRY_PICK_ROOT: case CANNOT_CHERRY_PICK_ROOT:
case NOT_FAST_FORWARD: case NOT_FAST_FORWARD:
case INVALID_PROJECT_CONFIGURATION:
case INVALID_PROJECT_CONFIGURATION_PLUGIN_VALUE_NOT_PERMITTED:
case INVALID_PROJECT_CONFIGURATION_PLUGIN_VALUE_NOT_EDITABLE:
case INVALID_PROJECT_CONFIGURATION_PARENT_PROJECT_NOT_FOUND:
case INVALID_PROJECT_CONFIGURATION_ROOT_PROJECT_CANNOT_HAVE_PARENT:
case SETTING_PARENT_PROJECT_ONLY_ALLOWED_BY_ADMIN:
// TODO(dborowitz): Reformat these messages to be more appropriate for // TODO(dborowitz): Reformat these messages to be more appropriate for
// short problem descriptions. // short problem descriptions.
problems.put(id, problems.put(id,

View File

@ -14,19 +14,18 @@
package com.google.gerrit.server.git.validators; package com.google.gerrit.server.git.validators;
import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.server.validators.ValidationException;
/**
* Exception that occurs during a validation step before merging changes.
* <p>
* Used by {@link MergeValidationListener}s provided by plugins. Messages should
* be considered human-readable.
*/
public class MergeValidationException extends ValidationException { public class MergeValidationException extends ValidationException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final CommitMergeStatus status;
public MergeValidationException(CommitMergeStatus status) { public MergeValidationException(String msg) {
super(status.toString()); super(msg);
this.status = status;
}
public CommitMergeStatus getStatus() {
return status;
} }
} }

View File

@ -30,7 +30,6 @@ import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.PluginConfig; import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.ProjectConfigEntry; import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@ -75,6 +74,26 @@ public class MergeValidators {
public static class ProjectConfigValidator implements public static class ProjectConfigValidator implements
MergeValidationListener { MergeValidationListener {
private static final String INVALID_CONFIG =
"Change contains an invalid project configuration.";
private static final String PARENT_NOT_FOUND =
"Change contains an invalid project configuration:\n"
+ "Parent project does not exist.";
private static final String PLUGIN_VALUE_NOT_EDITABLE =
"Change contains an invalid project configuration:\n"
+ "One of the plugin configuration parameters is not editable.";
private static final String PLUGIN_VALUE_NOT_PERMITTED =
"Change contains an invalid project configuration:\n"
+ "One of the plugin configuration parameters has a value that is not"
+ " permitted.";
private static final String ROOT_NO_PARENT =
"Change contains an invalid project configuration:\n"
+ "The root project cannot have a parent.";
private static final String SET_BY_ADMIN =
"Change contains a project configuration that changes the parent"
+ " project.\n"
+ "The change must be submitted by a Gerrit administrator.";
private final AllProjectsName allProjectsName; private final AllProjectsName allProjectsName;
private final ReviewDb db; private final ReviewDb db;
private final ProjectCache projectCache; private final ProjectCache projectCache;
@ -119,27 +138,23 @@ public class MergeValidators {
if (oldParent == null) { if (oldParent == null) {
// update of the 'All-Projects' project // update of the 'All-Projects' project
if (newParent != null) { if (newParent != null) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(ROOT_NO_PARENT);
INVALID_PROJECT_CONFIGURATION_ROOT_PROJECT_CANNOT_HAVE_PARENT);
} }
} else { } else {
if (!oldParent.equals(newParent)) { if (!oldParent.equals(newParent)) {
PatchSetApproval psa = PatchSetApproval psa =
approvalsUtil.getSubmitter(db, commit.notes(), patchSetId); approvalsUtil.getSubmitter(db, commit.notes(), patchSetId);
if (psa == null) { if (psa == null) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(SET_BY_ADMIN);
SETTING_PARENT_PROJECT_ONLY_ALLOWED_BY_ADMIN);
} }
final IdentifiedUser submitter = final IdentifiedUser submitter =
identifiedUserFactory.create(psa.getAccountId()); identifiedUserFactory.create(psa.getAccountId());
if (!submitter.getCapabilities().canAdministrateServer()) { if (!submitter.getCapabilities().canAdministrateServer()) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(SET_BY_ADMIN);
SETTING_PARENT_PROJECT_ONLY_ALLOWED_BY_ADMIN);
} }
if (projectCache.get(newParent) == null) { if (projectCache.get(newParent) == null) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(PARENT_NOT_FOUND);
INVALID_PROJECT_CONFIGURATION_PARENT_PROJECT_NOT_FOUND);
} }
} }
} }
@ -155,19 +170,16 @@ public class MergeValidators {
if ((value == null ? oldValue != null : !value.equals(oldValue)) && if ((value == null ? oldValue != null : !value.equals(oldValue)) &&
!configEntry.isEditable(destProject)) { !configEntry.isEditable(destProject)) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(PLUGIN_VALUE_NOT_EDITABLE);
INVALID_PROJECT_CONFIGURATION_PLUGIN_VALUE_NOT_EDITABLE);
} }
if (ProjectConfigEntry.Type.LIST.equals(configEntry.getType()) if (ProjectConfigEntry.Type.LIST.equals(configEntry.getType())
&& value != null && !configEntry.getPermittedValues().contains(value)) { && value != null && !configEntry.getPermittedValues().contains(value)) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(PLUGIN_VALUE_NOT_PERMITTED);
INVALID_PROJECT_CONFIGURATION_PLUGIN_VALUE_NOT_PERMITTED);
} }
} }
} catch (ConfigInvalidException | IOException e) { } catch (ConfigInvalidException | IOException e) {
throw new MergeValidationException(CommitMergeStatus. throw new MergeValidationException(INVALID_CONFIG);
INVALID_PROJECT_CONFIGURATION);
} }
} }
} }

@ -1 +1 @@
Subproject commit eea84e7e07ecf6ebb70ea5a6b0cde67f5a5576af Subproject commit 88b0984638857ac7139de83b18dc7cad23670b4d