Add extension point for plugins to validate comments before publishing

This allows to prevent publication of comments that fail validation.
This change includes only the validator interface.

There are three ways to publish comments:

A) On posting the review draft comments are read and, possibly together
   with new comments included in the PostReview request, published.
B) Running "git push" when the "Publish comments on push" user option
   is enabled (code in ReceiveCommits).
C) Via email (code in MailProcessor).

Subsequent changes will add the actual validation calls.

Before plugins can use this interface with a reasonable UX, the UI
needs to handle the new error in PostReview. Until then the UI will
show an error message that does not indicate the comment validation
failure.

Change-Id: Icd1a206dc6468a2e9b08cf3a39de9860bc879c4b
This commit is contained in:
Joerg Zieren
2019-06-06 09:57:53 +02:00
parent e24a7a4d2d
commit 91b4c321cf
4 changed files with 116 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
// Copyright (C) 2019 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.google.gerrit.extensions.validators;
import com.google.auto.value.AutoValue;
/**
* Holds a comment's text and {@link CommentType} in order to pass it to a validation plugin.
*
* @see CommentValidator
*/
@AutoValue
public abstract class CommentForValidation {
/** The type of comment. */
public enum CommentType {
/** A regular (inline) comment. */
INLINE_COMMENT,
/** A file comment. */
FILE_COMMENT,
/** A change message. */
CHANGE_MESSAGE
}
public static CommentForValidation create(CommentType type, String text) {
return new AutoValue_CommentForValidation(type, text);
}
public abstract CommentType getType();
public abstract String getText();
public CommentValidationFailure failValidation(String message) {
return CommentValidationFailure.create(this, message);
}
}

View File

@@ -0,0 +1,32 @@
// Copyright (C) 2019 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.google.gerrit.extensions.validators;
import com.google.auto.value.AutoValue;
/** A comment or review message was rejected by a {@link CommentValidator}. */
@AutoValue
public abstract class CommentValidationFailure {
static CommentValidationFailure create(
CommentForValidation commentForValidation, String message) {
return new AutoValue_CommentValidationFailure(commentForValidation, message);
}
/** Returns the offending comment. */
public abstract CommentForValidation getComment();
/** A friendly message set by the {@link CommentValidator}. */
public abstract String getMessage();
}

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2019 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.google.gerrit.extensions.validators;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
/**
* Validates review comments and messages. Rejecting any comment/message will prevent all comments
* from being published.
*/
@ExtensionPoint
public interface CommentValidator {
/**
* Validate the specified commits.
*
* @return An empty list if all commits are valid, or else a list of validation failures.
*/
ImmutableList<CommentValidationFailure> validateComments(
ImmutableList<CommentForValidation> comments);
}

View File

@@ -62,6 +62,7 @@ import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.systemstatus.MessageOfTheDay;
import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.extensions.webui.BranchWebLink;
import com.google.gerrit.extensions.webui.DiffWebLink;
import com.google.gerrit.extensions.webui.FileHistoryWebLink;
@@ -341,6 +342,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.bind(binder(), EventListener.class).to(EventsMetrics.class);
DynamicSet.setOf(binder(), UserScopedEventListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), CommentValidator.class);
DynamicSet.setOf(binder(), ChangeMessageModifier.class);
DynamicSet.setOf(binder(), RefOperationValidationListener.class);
DynamicSet.setOf(binder(), OnSubmitValidationListener.class);