Merge "ApprovalContext: Drop checkState that verified a wrong assumption"
diff --git a/java/com/google/gerrit/entities/SubmitRequirement.java b/java/com/google/gerrit/entities/SubmitRequirement.java
index df03fd5..13e0b53 100644
--- a/java/com/google/gerrit/entities/SubmitRequirement.java
+++ b/java/com/google/gerrit/entities/SubmitRequirement.java
@@ -15,6 +15,8 @@
package com.google.gerrit.entities;
import com.google.auto.value.AutoValue;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
import java.util.Optional;
/** Entity describing a requirement that should be met for a change to become submittable. */
@@ -62,6 +64,10 @@
return new AutoValue_SubmitRequirement.Builder();
}
+ public static TypeAdapter<SubmitRequirement> typeAdapter(Gson gson) {
+ return new AutoValue_SubmitRequirement.GsonTypeAdapter(gson);
+ }
+
@AutoValue.Builder
public abstract static class Builder {
diff --git a/java/com/google/gerrit/entities/SubmitRequirementExpression.java b/java/com/google/gerrit/entities/SubmitRequirementExpression.java
index c978347..2af1379 100644
--- a/java/com/google/gerrit/entities/SubmitRequirementExpression.java
+++ b/java/com/google/gerrit/entities/SubmitRequirementExpression.java
@@ -17,6 +17,8 @@
import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
import java.util.Optional;
/** Describe a applicability, blocking or override expression of a {@link SubmitRequirement}. */
@@ -41,4 +43,8 @@
/** Returns the underlying String representing this {@link SubmitRequirementExpression}. */
public abstract String expressionString();
+
+ public static TypeAdapter<SubmitRequirementExpression> typeAdapter(Gson gson) {
+ return new AutoValue_SubmitRequirementExpression.GsonTypeAdapter(gson);
+ }
}
diff --git a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
index f7a883e..58eb4ac 100644
--- a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
+++ b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
@@ -16,57 +16,78 @@
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
import java.util.Optional;
/** Result of evaluating a submit requirement expression on a given Change. */
@AutoValue
public abstract class SubmitRequirementExpressionResult {
- /**
- * Entity detailing the result of evaluating a Submit requirement expression. Contains an empty
- * {@link Optional} if {@link #status()} is equal to {@link Status#ERROR}.
- */
- public abstract Optional<PredicateResult> predicateResult();
+ /** Submit requirement expression for which this result is evaluated. */
+ public abstract SubmitRequirementExpression expression();
+ /** Status of evaluation. */
+ public abstract Status status();
+
+ /**
+ * Optional error message. Populated if the evaluator fails to evaluate the expression for a
+ * certain change.
+ */
public abstract Optional<String> errorMessage();
- public Status status() {
- if (predicateResult().isPresent()) {
- return predicateResult().get().status() ? Status.PASS : Status.FAIL;
- }
- return Status.ERROR;
- }
-
- public static SubmitRequirementExpressionResult create(PredicateResult predicateResult) {
- return new AutoValue_SubmitRequirementExpressionResult(
- Optional.of(predicateResult), Optional.empty());
- }
-
- public static SubmitRequirementExpressionResult error(String errorMessage) {
- return new AutoValue_SubmitRequirementExpressionResult(
- Optional.empty(), Optional.of(errorMessage));
- }
+ /**
+ * List leaf predicates that are fulfilled, for example the expression
+ *
+ * <p><i>label:code-review=+2 and branch:refs/heads/master</i>
+ *
+ * <p>has two leaf predicates:
+ *
+ * <ul>
+ * <li>label:code-review=+2
+ * <li>branch:refs/heads/master
+ * </ul>
+ *
+ * This method will return the leaf predicates that were fulfilled, for example if only the first
+ * predicate was fulfilled, the returned list will be equal to ["label:code-review=+2"].
+ */
+ public abstract ImmutableList<String> passingAtoms();
/**
- * Returns a list of leaf predicate results whose {@link PredicateResult#status()} is true. If
- * {@link #status()} is equal to {@link Status#ERROR}, an empty list is returned.
+ * List of leaf predicates that are not fulfilled. See {@link #passingAtoms()} for more details.
*/
- public ImmutableList<PredicateResult> getPassingAtoms() {
- if (predicateResult().isPresent()) {
- return predicateResult().get().getAtoms(/* status= */ true);
- }
- return ImmutableList.of();
+ public abstract ImmutableList<String> failingAtoms();
+
+ public static SubmitRequirementExpressionResult create(
+ SubmitRequirementExpression expression, PredicateResult predicateResult) {
+ return create(
+ expression,
+ predicateResult.status() ? Status.PASS : Status.FAIL,
+ predicateResult.getPassingAtoms(),
+ predicateResult.getFailingAtoms());
}
- /**
- * Returns a list of leaf predicate results whose {@link PredicateResult#status()} is false. If
- * {@link #status()} is equal to {@link Status#ERROR}, an empty list is returned.
- */
- public ImmutableList<PredicateResult> getFailingAtoms() {
- if (predicateResult().isPresent()) {
- return predicateResult().get().getAtoms(/* status= */ false);
- }
- return ImmutableList.of();
+ public static SubmitRequirementExpressionResult create(
+ SubmitRequirementExpression expression,
+ Status status,
+ ImmutableList<String> passingAtoms,
+ ImmutableList<String> failingAtoms) {
+ return new AutoValue_SubmitRequirementExpressionResult(
+ expression, status, Optional.empty(), passingAtoms, failingAtoms);
+ }
+
+ public static SubmitRequirementExpressionResult error(
+ SubmitRequirementExpression expression, String errorMessage) {
+ return new AutoValue_SubmitRequirementExpressionResult(
+ expression,
+ Status.ERROR,
+ Optional.of(errorMessage),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static TypeAdapter<SubmitRequirementExpressionResult> typeAdapter(Gson gson) {
+ return new AutoValue_SubmitRequirementExpressionResult.GsonTypeAdapter(gson);
}
public enum Status {
@@ -103,11 +124,25 @@
/** true if the predicate is passing for a given change. */
abstract boolean status();
+ /** Returns a list of leaf predicate results whose {@link PredicateResult#status()} is true. */
+ ImmutableList<String> getPassingAtoms() {
+ return getAtoms(/* status= */ true).stream()
+ .map(PredicateResult::predicateString)
+ .collect(ImmutableList.toImmutableList());
+ }
+
+ /** Returns a list of leaf predicate results whose {@link PredicateResult#status()} is false. */
+ ImmutableList<String> getFailingAtoms() {
+ return getAtoms(/* status= */ false).stream()
+ .map(PredicateResult::predicateString)
+ .collect(ImmutableList.toImmutableList());
+ }
+
/**
* Returns the list of leaf {@link PredicateResult} whose {@link #status()} is equal to the
* {@code status} parameter.
*/
- ImmutableList<PredicateResult> getAtoms(boolean status) {
+ private ImmutableList<PredicateResult> getAtoms(boolean status) {
ImmutableList.Builder<PredicateResult> atomsList = ImmutableList.builder();
getAtomsRecursively(atomsList, status);
return atomsList.build();
diff --git a/java/com/google/gerrit/entities/SubmitRequirementResult.java b/java/com/google/gerrit/entities/SubmitRequirementResult.java
index e28c86f..e1d5f39 100644
--- a/java/com/google/gerrit/entities/SubmitRequirementResult.java
+++ b/java/com/google/gerrit/entities/SubmitRequirementResult.java
@@ -16,11 +16,17 @@
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
/** Result of evaluating a {@link SubmitRequirement} on a given Change. */
@AutoValue
public abstract class SubmitRequirementResult {
+ /** Submit requirement for which this result is evaluated. */
+ public abstract SubmitRequirement submitRequirement();
+
/** Result of evaluating a {@link SubmitRequirement#applicabilityExpression()} on a change. */
public abstract Optional<SubmitRequirementExpressionResult> applicabilityExpressionResult();
@@ -32,6 +38,9 @@
/** Result of evaluating a {@link SubmitRequirement#overrideExpression()} ()} on a change. */
public abstract Optional<SubmitRequirementExpressionResult> overrideExpressionResult();
+ /** SHA-1 of the patchset commit ID for which the submit requirement was evaluated. */
+ public abstract ObjectId patchSetCommitId();
+
@Memoized
public Status status() {
if (assertError(submittabilityExpressionResult())
@@ -53,6 +62,10 @@
return new AutoValue_SubmitRequirementResult.Builder();
}
+ public static TypeAdapter<SubmitRequirementResult> typeAdapter(Gson gson) {
+ return new AutoValue_SubmitRequirementResult.GsonTypeAdapter(gson);
+ }
+
public enum Status {
/** Submit requirement is fulfilled. */
SATISFIED,
@@ -84,6 +97,7 @@
@AutoValue.Builder
public abstract static class Builder {
+ public abstract Builder submitRequirement(SubmitRequirement submitRequirement);
public abstract Builder applicabilityExpressionResult(
Optional<SubmitRequirementExpressionResult> value);
@@ -93,6 +107,8 @@
public abstract Builder overrideExpressionResult(
Optional<SubmitRequirementExpressionResult> value);
+ public abstract Builder patchSetCommitId(ObjectId value);
+
public abstract SubmitRequirementResult build();
}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementExpressionResultSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementExpressionResultSerializer.java
new file mode 100644
index 0000000..4e997b4
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementExpressionResultSerializer.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 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.server.cache.serialize.entities;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.SubmitRequirementExpression;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult;
+import com.google.gerrit.server.cache.proto.Cache.SubmitRequirementExpressionResultProto;
+
+/**
+ * Serializer of a {@link SubmitRequirementExpressionResult} to {@link
+ * SubmitRequirementExpressionResultProto}.
+ */
+public class SubmitRequirementExpressionResultSerializer {
+ public static SubmitRequirementExpressionResult deserialize(
+ SubmitRequirementExpressionResultProto proto) {
+ return SubmitRequirementExpressionResult.create(
+ SubmitRequirementExpression.create(proto.getExpression()),
+ SubmitRequirementExpressionResult.Status.valueOf(proto.getStatus()),
+ proto.getPassingAtomsList().stream().collect(ImmutableList.toImmutableList()),
+ proto.getFailingAtomsList().stream().collect(ImmutableList.toImmutableList()));
+ }
+
+ public static SubmitRequirementExpressionResultProto serialize(
+ SubmitRequirementExpressionResult r) {
+ return SubmitRequirementExpressionResultProto.newBuilder()
+ .setExpression(r.expression().expressionString())
+ .setStatus(r.status().name())
+ .addAllPassingAtoms(r.passingAtoms())
+ .addAllFailingAtoms(r.failingAtoms())
+ .build();
+ }
+}
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index e9c9946..ff8d8cb 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -63,7 +63,6 @@
import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.entities.SubmitRequirementExpression;
import com.google.gerrit.entities.SubmitRequirementExpressionResult;
-import com.google.gerrit.entities.SubmitRequirementExpressionResult.PredicateResult;
import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.SubmitTypeRecord;
import com.google.gerrit.exceptions.StorageException;
@@ -411,10 +410,8 @@
SubmitRequirementExpressionInfo info = new SubmitRequirementExpressionInfo();
info.expression = expression.expressionString();
info.fulfilled = result.status().equals(SubmitRequirementExpressionResult.Status.PASS);
- info.passingAtoms =
- result.getPassingAtoms().stream().map(PredicateResult::predicateString).collect(toList());
- info.failingAtoms =
- result.getFailingAtoms().stream().map(PredicateResult::predicateString).collect(toList());
+ info.passingAtoms = result.passingAtoms();
+ info.failingAtoms = result.failingAtoms();
return info;
}
diff --git a/java/com/google/gerrit/server/config/PluginConfigFactory.java b/java/com/google/gerrit/server/config/PluginConfigFactory.java
index 2d0f9a5..c49e928 100644
--- a/java/com/google/gerrit/server/config/PluginConfigFactory.java
+++ b/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -28,13 +28,11 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.storage.file.FileSnapshot;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
@@ -52,7 +50,6 @@
private final SecureStore secureStore;
private final Map<String, Config> pluginConfigs;
- private volatile FileSnapshot cfgSnapshot;
private volatile Config cfg;
@Inject
@@ -69,7 +66,6 @@
this.secureStore = secureStore;
this.pluginConfigs = new HashMap<>();
- this.cfgSnapshot = FileSnapshot.save(site.gerrit_config.toFile());
this.cfg = cfgProvider.get();
}
@@ -103,12 +99,10 @@
* @return the plugin configuration from the 'gerrit.config' file
*/
public PluginConfig getFromGerritConfig(String pluginName, boolean refresh) {
- if (refresh && secureStore.isOutdated()) {
- secureStore.reload();
- }
- File configFile = site.gerrit_config.toFile();
- if (refresh && cfgSnapshot.isModified(configFile)) {
- cfgSnapshot = FileSnapshot.save(configFile);
+ if (refresh) {
+ if (secureStore.isOutdated()) {
+ secureStore.reload();
+ }
cfg = cfgProvider.get();
}
return PluginConfig.createFromGerritConfig(pluginName, cfg);
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteJson.java b/java/com/google/gerrit/server/notedb/ChangeNoteJson.java
index 483b2e9..4c41a12 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteJson.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteJson.java
@@ -14,9 +14,17 @@
package com.google.gerrit.server.notedb;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.EntitiesAdapterFactory;
+import com.google.gerrit.json.EnumTypeAdapterFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import java.io.IOException;
import java.sql.Timestamp;
@Singleton
@@ -26,6 +34,11 @@
static Gson newGson() {
return new GsonBuilder()
.registerTypeAdapter(Timestamp.class, new CommentTimestampAdapter().nullSafe())
+ .registerTypeAdapterFactory(new EnumTypeAdapterFactory())
+ .registerTypeAdapterFactory(EntitiesAdapterFactory.create())
+ .registerTypeAdapter(
+ new TypeLiteral<ImmutableList<String>>() {}.getType(),
+ new ImmutableListAdapter().nullSafe())
.setPrettyPrinting()
.create();
}
@@ -33,4 +46,27 @@
public Gson getGson() {
return gson;
}
+
+ static class ImmutableListAdapter extends TypeAdapter<ImmutableList<String>> {
+
+ @Override
+ public void write(JsonWriter out, ImmutableList<String> value) throws IOException {
+ out.beginArray();
+ for (String v : value) {
+ out.value(v);
+ }
+ out.endArray();
+ }
+
+ @Override
+ public ImmutableList<String> read(JsonReader in) throws IOException {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ in.beginArray();
+ while (in.hasNext()) {
+ builder.add(in.nextString());
+ }
+ in.endArray();
+ return builder.build();
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 5daf28c..6684493 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -50,6 +50,7 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
@@ -413,6 +414,16 @@
}
/**
+ * Returns the evaluated submit requirements for the change. We only intend to store submit
+ * requirements in NoteDb for closed changes, hence the result will be an empty list for active
+ * changes, or a list of submit requirements results otherwise. For closed changes, the results
+ * represent the state of evaluating submit requirements for this change when it was merged.
+ */
+ public ImmutableList<SubmitRequirementResult> getSubmitRequirementsResult() {
+ return state.submitRequirementsResult();
+ }
+
+ /**
* @return an ImmutableSet of Account.Ids of all users that have been assigned to this change. The
* order of the set is the order in which they were assigned.
*/
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 8be0d82..2a53c29 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -68,6 +68,7 @@
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
@@ -125,6 +126,7 @@
private final List<AssigneeStatusUpdate> assigneeUpdates;
private final List<SubmitRecord> submitRecords;
private final ListMultimap<ObjectId, HumanComment> humanComments;
+ private final List<SubmitRequirementResult> submitRequirementResults;
private final Map<PatchSet.Id, PatchSet.Builder> patchSets;
private final Set<PatchSet.Id> deletedPatchSets;
private final Map<PatchSet.Id, PatchSetState> patchSetStates;
@@ -187,6 +189,7 @@
submitRecords = Lists.newArrayListWithExpectedSize(1);
allChangeMessages = new ArrayList<>();
humanComments = MultimapBuilder.hashKeys().arrayListValues().build();
+ submitRequirementResults = new ArrayList<>();
patchSets = new HashMap<>();
deletedPatchSets = new HashSet<>();
patchSetStates = new HashMap<>();
@@ -259,6 +262,7 @@
submitRecords,
buildAllMessages(),
humanComments,
+ submitRequirementResults,
firstNonNull(isPrivate, false),
firstNonNull(workInProgress, false),
firstNonNull(hasReviewStarted, true),
@@ -774,6 +778,9 @@
for (HumanComment c : e.getValue().getEntities()) {
humanComments.put(e.getKey(), c);
}
+ for (SubmitRequirementResult sr : e.getValue().getSubmitRequirementsResult()) {
+ submitRequirementResults.add(sr);
+ }
}
for (PatchSet.Builder b : patchSets.values()) {
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 33bc039..e7da025 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -44,6 +44,7 @@
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.converter.ChangeMessageProtoConverter;
import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
import com.google.gerrit.entities.converter.PatchSetProtoConverter;
@@ -60,10 +61,14 @@
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerStatusUpdateProto;
+import com.google.gerrit.server.cache.proto.Cache.SubmitRequirementResultProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.entities.SubmitRequirementExpressionResultSerializer;
+import com.google.gerrit.server.cache.serialize.entities.SubmitRequirementSerializer;
import com.google.gerrit.server.index.change.ChangeField.StoredSubmitRecord;
import com.google.gson.Gson;
+import com.google.protobuf.Descriptors.FieldDescriptor;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
@@ -128,6 +133,7 @@
List<SubmitRecord> submitRecords,
List<ChangeMessage> changeMessages,
ListMultimap<ObjectId, HumanComment> publishedComments,
+ List<SubmitRequirementResult> submitRequirementResults,
boolean isPrivate,
boolean workInProgress,
boolean reviewStarted,
@@ -181,6 +187,7 @@
.submitRecords(submitRecords)
.changeMessages(changeMessages)
.publishedComments(publishedComments)
+ .submitRequirementsResult(submitRequirementResults)
.updateCount(updateCount)
.mergedOn(mergedOn)
.build();
@@ -326,6 +333,8 @@
abstract ImmutableListMultimap<ObjectId, HumanComment> publishedComments();
+ abstract ImmutableList<SubmitRequirementResult> submitRequirementsResult();
+
abstract int updateCount();
@Nullable
@@ -404,6 +413,7 @@
.submitRecords(ImmutableList.of())
.changeMessages(ImmutableList.of())
.publishedComments(ImmutableListMultimap.of())
+ .submitRequirementsResult(ImmutableList.of())
.updateCount(0);
}
@@ -445,6 +455,9 @@
abstract Builder publishedComments(ListMultimap<ObjectId, HumanComment> publishedComments);
+ abstract Builder submitRequirementsResult(
+ List<SubmitRequirementResult> submitRequirementsResult);
+
abstract Builder updateCount(int updateCount);
abstract Builder mergedOn(Timestamp mergedOn);
@@ -465,6 +478,11 @@
private static final Converter<String, ReviewerStateInternal> REVIEWER_STATE_CONVERTER =
Enums.stringConverter(ReviewerStateInternal.class);
+ private static final FieldDescriptor SR_APPLICABILITY_EXPR_RESULT_FIELD =
+ SubmitRequirementResultProto.getDescriptor().findFieldByNumber(2);
+ private static final FieldDescriptor SR_OVERRIDE_EXPR_RESULT_FIELD =
+ SubmitRequirementResultProto.getDescriptor().findFieldByNumber(4);
+
@Override
public byte[] serialize(ChangeNotesState object) {
checkArgument(object.metaId() != null, "meta ID is required in: %s", object);
@@ -519,6 +537,9 @@
.changeMessages()
.forEach(m -> b.addChangeMessage(ChangeMessageProtoConverter.INSTANCE.toProto(m)));
object.publishedComments().values().forEach(c -> b.addPublishedComment(GSON.toJson(c)));
+ object
+ .submitRequirementsResult()
+ .forEach(sr -> b.addSubmitRequirementResult(toSubmitRequirementResultProto(sr)));
b.setUpdateCount(object.updateCount());
if (object.mergedOn() != null) {
b.setMergedOnMillis(object.mergedOn().getTime());
@@ -613,6 +634,53 @@
return builder.build();
}
+ private static SubmitRequirementResultProto toSubmitRequirementResultProto(
+ SubmitRequirementResult r) {
+ SubmitRequirementResultProto.Builder builder = SubmitRequirementResultProto.newBuilder();
+ builder
+ .setSubmitRequirement(SubmitRequirementSerializer.serialize(r.submitRequirement()))
+ .setCommit(ObjectIdConverter.create().toByteString(r.patchSetCommitId()));
+ if (r.applicabilityExpressionResult().isPresent()) {
+ builder.setApplicabilityExpressionResult(
+ SubmitRequirementExpressionResultSerializer.serialize(
+ r.applicabilityExpressionResult().get()));
+ }
+ builder.setSubmittabilityExpressionResult(
+ SubmitRequirementExpressionResultSerializer.serialize(
+ r.submittabilityExpressionResult()));
+ if (r.overrideExpressionResult().isPresent()) {
+ builder.setOverrideExpressionResult(
+ SubmitRequirementExpressionResultSerializer.serialize(
+ r.overrideExpressionResult().get()));
+ }
+ return builder.build();
+ }
+
+ private static SubmitRequirementResult toSubmitRequirementResult(
+ SubmitRequirementResultProto proto) {
+ SubmitRequirementResult.Builder builder =
+ SubmitRequirementResult.builder()
+ .patchSetCommitId(ObjectIdConverter.create().fromByteString(proto.getCommit()))
+ .submitRequirement(
+ SubmitRequirementSerializer.deserialize(proto.getSubmitRequirement()));
+ if (proto.hasField(SR_APPLICABILITY_EXPR_RESULT_FIELD)) {
+ builder.applicabilityExpressionResult(
+ Optional.of(
+ SubmitRequirementExpressionResultSerializer.deserialize(
+ proto.getApplicabilityExpressionResult())));
+ }
+ builder.submittabilityExpressionResult(
+ SubmitRequirementExpressionResultSerializer.deserialize(
+ proto.getSubmittabilityExpressionResult()));
+ if (proto.hasField(SR_OVERRIDE_EXPR_RESULT_FIELD)) {
+ builder.overrideExpressionResult(
+ Optional.of(
+ SubmitRequirementExpressionResultSerializer.deserialize(
+ proto.getOverrideExpressionResult())));
+ }
+ return builder.build();
+ }
+
@Override
public ChangeNotesState deserialize(byte[] in) {
ChangeNotesStateProto proto = Protos.parseUnchecked(ChangeNotesStateProto.parser(), in);
@@ -658,6 +726,10 @@
proto.getPublishedCommentList().stream()
.map(r -> GSON.fromJson(r, HumanComment.class))
.collect(toImmutableListMultimap(HumanComment::getCommitId, c -> c)))
+ .submitRequirementsResult(
+ proto.getSubmitRequirementResultList().stream()
+ .map(sr -> toSubmitRequirementResult(sr))
+ .collect(toImmutableList()))
.updateCount(proto.getUpdateCount())
.mergedOn(proto.getHasMergedOn() ? new Timestamp(proto.getMergedOnMillis()) : null);
return b.build();
diff --git a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
index bf2cf07..44475db 100644
--- a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
+++ b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
@@ -16,7 +16,9 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.HumanComment;
+import com.google.gerrit.entities.SubmitRequirementResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -34,6 +36,8 @@
private final HumanComment.Status status;
private String pushCert;
+ private ImmutableList<SubmitRequirementResult> submitRequirementsResult;
+
ChangeRevisionNote(
ChangeNoteJson noteJson, ObjectReader reader, ObjectId noteId, HumanComment.Status status) {
super(reader, noteId);
@@ -41,6 +45,11 @@
this.status = status;
}
+ public ImmutableList<SubmitRequirementResult> getSubmitRequirementsResult() {
+ checkParsed();
+ return submitRequirementsResult;
+ }
+
public String getPushCert() {
checkParsed();
return pushCert;
@@ -52,20 +61,24 @@
MutableInteger p = new MutableInteger();
p.value = offset;
- HumanCommentsRevisionNoteData data = parseJson(noteJson, raw, p.value);
+ ChangeRevisionNoteData data = parseJson(noteJson, raw, p.value);
if (status == HumanComment.Status.PUBLISHED) {
pushCert = data.pushCert;
} else {
pushCert = null;
}
+ this.submitRequirementsResult =
+ data.submitRequirementResults == null
+ ? ImmutableList.of()
+ : ImmutableList.copyOf(data.submitRequirementResults);
return data.comments;
}
- private HumanCommentsRevisionNoteData parseJson(ChangeNoteJson noteUtil, byte[] raw, int offset)
+ private ChangeRevisionNoteData parseJson(ChangeNoteJson noteUtil, byte[] raw, int offset)
throws IOException {
try (InputStream is = new ByteArrayInputStream(raw, offset, raw.length - offset);
Reader r = new InputStreamReader(is, UTF_8)) {
- return noteUtil.getGson().fromJson(r, HumanCommentsRevisionNoteData.class);
+ return noteUtil.getGson().fromJson(r, ChangeRevisionNoteData.class);
}
}
}
diff --git a/java/com/google/gerrit/server/notedb/HumanCommentsRevisionNoteData.java b/java/com/google/gerrit/server/notedb/ChangeRevisionNoteData.java
similarity index 78%
rename from java/com/google/gerrit/server/notedb/HumanCommentsRevisionNoteData.java
rename to java/com/google/gerrit/server/notedb/ChangeRevisionNoteData.java
index e570412..8e33023 100644
--- a/java/com/google/gerrit/server/notedb/HumanCommentsRevisionNoteData.java
+++ b/java/com/google/gerrit/server/notedb/ChangeRevisionNoteData.java
@@ -15,14 +15,17 @@
package com.google.gerrit.server.notedb;
import com.google.gerrit.entities.HumanComment;
+import com.google.gerrit.entities.SubmitRequirementResult;
import java.util.List;
/**
* Holds the raw data of a RevisionNote.
*
- * <p>It is intended for deserialization from JSON only. It is used for human comments only.
+ * <p>It is intended for deserialization from JSON only. It is used for human comments. Submit
+ * requirements are also stored but only for closed changes.
*/
-class HumanCommentsRevisionNoteData {
+class ChangeRevisionNoteData {
String pushCert;
List<HumanComment> comments;
+ List<SubmitRequirementResult> submitRequirementResults;
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 8b9d2856..971e0a8 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -65,6 +65,7 @@
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.entities.SubmissionId;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.server.CurrentUser;
@@ -78,6 +79,7 @@
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
@@ -129,6 +131,7 @@
private final Map<Account.Id, ReviewerStateInternal> reviewers = new LinkedHashMap<>();
private final Map<Address, ReviewerStateInternal> reviewersByEmail = new LinkedHashMap<>();
private final List<HumanComment> comments = new ArrayList<>();
+ private final List<SubmitRequirementResult> submitRequirementResults = new ArrayList<>();
private String commitSubject;
private String subject;
@@ -302,6 +305,10 @@
this.psDescription = psDescription;
}
+ public void putSubmitRequirementResults(Collection<SubmitRequirementResult> rs) {
+ submitRequirementResults.addAll(rs);
+ }
+
public void putComment(HumanComment.Status status, HumanComment c) {
verifyComment(c);
createDraftUpdateIfNull();
@@ -488,7 +495,7 @@
/** @return the tree id for the updated tree */
private ObjectId storeRevisionNotes(RevWalk rw, ObjectInserter inserter, ObjectId curr)
throws ConfigInvalidException, IOException {
- if (comments.isEmpty() && pushCert == null) {
+ if (submitRequirementResults.isEmpty() && comments.isEmpty() && pushCert == null) {
return null;
}
RevisionNoteMap<ChangeRevisionNote> rnm = getRevisionNoteMap(rw, curr);
@@ -498,6 +505,9 @@
c.tag = tag;
cache.get(c.getCommitId()).putComment(c);
}
+ for (SubmitRequirementResult sr : submitRequirementResults) {
+ cache.get(sr.patchSetCommitId()).putSubmitRequirementResult(sr);
+ }
if (pushCert != null) {
checkState(commit != null);
cache.get(ObjectId.fromString(commit)).setPushCertificate(pushCert);
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
index 3c1d359..7998476 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
@@ -22,16 +22,20 @@
import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.SubmitRequirementResult;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
@@ -60,11 +64,16 @@
}
}
+ /** Submit requirements are sorted w.r.t. their names before storing in NoteDb. */
+ private final Comparator<SubmitRequirementResult> SUBMIT_REQUIREMENT_RESULT_COMPARATOR =
+ Comparator.comparing(sr -> sr.submitRequirement().name());
+
final byte[] baseRaw;
private final List<? extends Comment> baseComments;
final Map<Comment.Key, Comment> put;
private final Set<Comment.Key> delete;
+ private List<SubmitRequirementResult> submitRequirementResults;
private String pushCert;
private RevisionNoteBuilder(RevisionNote<? extends Comment> base) {
@@ -81,6 +90,7 @@
put = new HashMap<>();
pushCert = null;
}
+ submitRequirementResults = new ArrayList<>();
delete = new HashSet<>();
}
@@ -99,6 +109,10 @@
put.put(comment.key, comment);
}
+ void putSubmitRequirementResult(SubmitRequirementResult result) {
+ submitRequirementResults.add(result);
+ }
+
void deleteComment(Comment.Key key) {
checkArgument(!put.containsKey(key), "cannot both delete and put %s", key);
delete.add(key);
@@ -126,13 +140,19 @@
private void buildNoteJson(ChangeNoteJson noteUtil, OutputStream out) throws IOException {
ListMultimap<Integer, Comment> comments = buildCommentMap();
- if (comments.isEmpty() && pushCert == null) {
+ if (submitRequirementResults.isEmpty() && comments.isEmpty() && pushCert == null) {
return;
}
RevisionNoteData data = new RevisionNoteData();
data.comments = COMMENT_ORDER.sortedCopy(comments.values());
data.pushCert = pushCert;
+ if (!submitRequirementResults.isEmpty()) {
+ data.submitRequirementResults =
+ submitRequirementResults.stream()
+ .sorted(SUBMIT_REQUIREMENT_RESULT_COMPARATOR)
+ .collect(Collectors.toList());
+ }
try (OutputStreamWriter osw = new OutputStreamWriter(out, UTF_8)) {
noteUtil.getGson().toJson(data, osw);
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteData.java b/java/com/google/gerrit/server/notedb/RevisionNoteData.java
index da15b34..c8770f1 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteData.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteData.java
@@ -15,15 +15,17 @@
package com.google.gerrit.server.notedb;
import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.SubmitRequirementResult;
import java.util.List;
/**
* Holds the raw data of a RevisionNote.
*
* <p>It is intended for serialization to JSON only. It is used for human comments and robot
- * comments.
+ * comments, as well as for storing submit requirements.
*/
class RevisionNoteData {
String pushCert;
List<Comment> comments;
+ List<SubmitRequirementResult> submitRequirementResults;
}
diff --git a/java/com/google/gerrit/server/notedb/StoreSubmitRequirementsOp.java b/java/com/google/gerrit/server/notedb/StoreSubmitRequirementsOp.java
new file mode 100644
index 0000000..47948d7
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/StoreSubmitRequirementsOp.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 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.server.notedb;
+
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.update.BatchUpdateOp;
+import com.google.gerrit.server.update.ChangeContext;
+
+/** A {@link BatchUpdateOp} that stores the evaluated submit requirements of a change in NoteDb. */
+public class StoreSubmitRequirementsOp implements BatchUpdateOp {
+ private final ChangeData.Factory changeDataFactory;
+
+ public StoreSubmitRequirementsOp(ChangeData.Factory changeDataFactory) {
+ this.changeDataFactory = changeDataFactory;
+ }
+
+ @Override
+ public boolean updateChange(ChangeContext ctx) throws Exception {
+ Change change = ctx.getChange();
+ ChangeData changeData = changeDataFactory.create(change);
+ ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
+ update.putSubmitRequirementResults(changeData.submitRequirements().values());
+ return !changeData.submitRequirements().isEmpty();
+ }
+}
diff --git a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluator.java b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluator.java
index 1d154dd..65d9d9e 100644
--- a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluator.java
+++ b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluator.java
@@ -66,6 +66,8 @@
: Optional.empty();
return SubmitRequirementResult.builder()
+ .submitRequirement(sr)
+ .patchSetCommitId(cd.currentPatchSet().commitId())
.submittabilityExpressionResult(blockingResult)
.applicabilityExpressionResult(applicabilityResult)
.overrideExpressionResult(overrideResult)
@@ -79,9 +81,9 @@
Predicate<ChangeData> predicate =
changeQueryBuilderProvider.get().parse(expression.expressionString());
PredicateResult predicateResult = evaluatePredicateTree(predicate, changeData);
- return SubmitRequirementExpressionResult.create(predicateResult);
+ return SubmitRequirementExpressionResult.create(expression, predicateResult);
} catch (QueryParseException e) {
- return SubmitRequirementExpressionResult.error(e.getMessage());
+ return SubmitRequirementExpressionResult.error(expression, e.getMessage());
}
}
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 2b4fb3b..363cdca 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -67,6 +67,7 @@
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.StoreSubmitRequirementsOp;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.SubmitRuleOptions;
@@ -96,6 +97,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
@@ -654,6 +657,16 @@
toSubmit, updateOrderCalculator, submoduleCommits, subscriptionGraph, dryrun);
this.allProjects = updateOrderCalculator.getProjectsInOrder();
List<BatchUpdate> batchUpdates = orm.batchUpdates(allProjects);
+ // Group batch updates by project
+ Map<Project.NameKey, BatchUpdate> batchUpdatesByProject =
+ batchUpdates.stream().collect(Collectors.toMap(b -> b.getProject(), Function.identity()));
+ for (Map.Entry<Change.Id, ChangeData> entry : cs.changesById().entrySet()) {
+ Project.NameKey project = entry.getValue().project();
+ Change.Id changeId = entry.getKey();
+ batchUpdatesByProject
+ .get(project)
+ .addOp(changeId, new StoreSubmitRequirementsOp(changeDataFactory));
+ }
try {
submissionExecutor.setAdditionalBatchUpdateListeners(
ImmutableList.of(new SubmitStrategyListener(submitInput, strategies, commitStatus)));
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 976e828..c08aa7f 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -69,6 +69,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.collect.MoreCollectors;
import com.google.common.truth.ThrowableSubject;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.ChangeIndexedCounter;
@@ -104,6 +105,8 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.entities.SubmitRequirementExpression;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
@@ -172,6 +175,7 @@
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.patch.DiffSummary;
import com.google.gerrit.server.patch.DiffSummaryKey;
import com.google.gerrit.server.patch.IntraLineDiff;
@@ -4287,6 +4291,40 @@
}
@Test
+ public void submitRequirement_storedForClosedChanges() throws Exception {
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange("Add a file", "foo", "content");
+ String changeId = r.getChangeId();
+
+ voteLabel(changeId, "code-review", 2);
+
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+
+ RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
+ revision.review(ReviewInput.approve());
+ revision.submit();
+
+ ChangeNotes notes = notesFactory.create(project, r.getChange().getId());
+
+ SubmitRequirementResult result =
+ notes.getSubmitRequirementsResult().stream().collect(MoreCollectors.onlyElement());
+ assertThat(result.status()).isEqualTo(SubmitRequirementResult.Status.SATISFIED);
+ assertThat(result.submittabilityExpressionResult().status())
+ .isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
+ assertThat(result.submittabilityExpressionResult().expression().expressionString())
+ .isEqualTo("label:code-review=+2");
+ }
+
+ @Test
public void fourByteEmoji() throws Exception {
// U+1F601 GRINNING FACE WITH SMILING EYES
String smile = new String(Character.toChars(0x1f601));
diff --git a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
index 23047a4..e848cef 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
@@ -29,7 +29,6 @@
import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.entities.SubmitRequirementExpression;
import com.google.gerrit.entities.SubmitRequirementExpressionResult;
-import com.google.gerrit.entities.SubmitRequirementExpressionResult.PredicateResult;
import com.google.gerrit.entities.SubmitRequirementExpressionResult.Status;
import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -102,25 +101,14 @@
assertThat(result.status()).isEqualTo(Status.PASS);
- assertThat(result.getPassingAtoms())
- .containsExactly(
- PredicateResult.builder()
- .predicateString(String.format("project:%s", project.get()))
- .status(true)
- .build(),
- PredicateResult.builder()
- .predicateString("message:\"Fix a bug\"")
- .status(true)
- .build());
+ assertThat(result.passingAtoms())
+ .containsExactly(String.format("project:%s", project.get()), "message:\"Fix a bug\"");
- assertThat(result.getFailingAtoms())
+ assertThat(result.failingAtoms())
.containsExactly(
- PredicateResult.builder()
- // TODO(ghareeb): querying "branch:" creates a RefPredicate. Fix names so that they
- // match
- .predicateString(String.format("ref:refs/heads/foo"))
- .status(false)
- .build());
+ // TODO(ghareeb): querying "branch:" creates a RefPredicate. Fix names so that they
+ // match
+ String.format("ref:refs/heads/foo"));
}
@Test
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index feff89c..ecdb03d 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -36,6 +36,10 @@
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementExpression;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.converter.ChangeMessageProtoConverter;
import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
import com.google.gerrit.entities.converter.PatchSetProtoConverter;
@@ -52,6 +56,9 @@
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerStatusUpdateProto;
+import com.google.gerrit.server.cache.proto.Cache.SubmitRequirementExpressionResultProto;
+import com.google.gerrit.server.cache.proto.Cache.SubmitRequirementProto;
+import com.google.gerrit.server.cache.proto.Cache.SubmitRequirementResultProto;
import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.gerrit.server.notedb.ChangeNotesState.ChangeColumns;
import com.google.gerrit.server.notedb.ChangeNotesState.Serializer;
@@ -671,6 +678,69 @@
}
@Test
+ public void serializeSubmitRequirementsResult() throws Exception {
+ assertRoundTrip(
+ newBuilder()
+ .submitRequirementsResult(
+ ImmutableList.of(
+ SubmitRequirementResult.builder()
+ .patchSetCommitId(
+ ObjectId.fromString("26e50c7d315a33a13e5cc00902781fa876bc36cd"))
+ .submitRequirement(
+ SubmitRequirement.builder()
+ .setName("Code-Review")
+ .setApplicabilityExpression(
+ SubmitRequirementExpression.of("project:foo"))
+ .setSubmittabilityExpression(
+ SubmitRequirementExpression.create("label:code-review=+2"))
+ .setAllowOverrideInChildProjects(false)
+ .build())
+ .applicabilityExpressionResult(
+ Optional.of(
+ SubmitRequirementExpressionResult.create(
+ SubmitRequirementExpression.create("project:foo"),
+ SubmitRequirementExpressionResult.Status.PASS,
+ ImmutableList.of("project:foo"),
+ ImmutableList.of())))
+ .submittabilityExpressionResult(
+ SubmitRequirementExpressionResult.create(
+ SubmitRequirementExpression.create("label:code-review=+2"),
+ SubmitRequirementExpressionResult.Status.FAIL,
+ ImmutableList.of(),
+ ImmutableList.of("label:code-review=+2")))
+ .build()))
+ .build(),
+ newProtoBuilder()
+ .addSubmitRequirementResult(
+ SubmitRequirementResultProto.newBuilder()
+ .setCommit(
+ ObjectIdConverter.create()
+ .toByteString(
+ ObjectId.fromString("26e50c7d315a33a13e5cc00902781fa876bc36cd")))
+ .setSubmitRequirement(
+ SubmitRequirementProto.newBuilder()
+ .setName("Code-Review")
+ .setApplicabilityExpression("project:foo")
+ .setSubmittabilityExpression("label:code-review=+2")
+ .setAllowOverrideInChildProjects(false)
+ .build())
+ .setApplicabilityExpressionResult(
+ SubmitRequirementExpressionResultProto.newBuilder()
+ .setExpression("project:foo")
+ .setStatus("PASS")
+ .addPassingAtoms("project:foo")
+ .build())
+ .setSubmittabilityExpressionResult(
+ SubmitRequirementExpressionResultProto.newBuilder()
+ .setExpression("label:code-review=+2")
+ .setStatus("FAIL")
+ .addFailingAtoms("label:code-review=+2")
+ .build())
+ .build())
+ .build());
+ }
+
+ @Test
public void serializeAssigneeUpdates() throws Exception {
assertRoundTrip(
newBuilder()
@@ -842,6 +912,9 @@
.put(
"publishedComments",
new TypeLiteral<ImmutableListMultimap<ObjectId, HumanComment>>() {}.getType())
+ .put(
+ "submitRequirementsResult",
+ new TypeLiteral<ImmutableList<SubmitRequirementResult>>() {}.getType())
.put("updateCount", int.class)
.put("mergedOn", Timestamp.class)
.build());
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
index a1201c9..9e24a09 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
@@ -249,7 +249,7 @@
<div class="content messageContent">
<div class="message hideOnOpen">[[_messageContentCollapsed]]</div>
<gr-formatted-text
- no-trailing-margin=""
+ noTrailingMargin
class="message hideOnCollapsed"
content="[[_messageContentExpanded]]"
config="[[_projectConfig.commentlinks]]"
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index b348eb5..f27a383 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -588,10 +588,10 @@
.value="${this.result}"
></gr-endpoint-param>
<gr-formatted-text
- no-trailing-margin=""
+ noTrailingMargin
class="message"
- content="${this.result.message}"
- config="${this.repoConfig}"
+ .content="${this.result.message}"
+ .config="${this.repoConfig?.commentlinks}"
></gr-formatted-text>
</gr-endpoint-decorator>
`;
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts
index 6b5553d..0193197 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts
@@ -42,7 +42,7 @@
@property({type: Object})
config?: CommentLinks;
- @property({type: Boolean})
+ @property({type: Boolean, reflect: true})
noTrailingMargin = false;
private readonly reporting = appContext.reportingService;
@@ -67,10 +67,10 @@
blockquote {
max-width: var(--gr-formatted-text-prose-max-width, none);
}
- :host(.noTrailingMargin) p:last-child,
- :host(.noTrailingMargin) ul:last-child,
- :host(.noTrailingMargin) blockquote:last-child,
- :host(.noTrailingMargin) gr-linked-text.pre:last-child {
+ :host([noTrailingMargin]) p:last-child,
+ :host([noTrailingMargin]) ul:last-child,
+ :host([noTrailingMargin]) blockquote:last-child,
+ :host([noTrailingMargin]) gr-linked-text.pre:last-child {
margin: 0;
}
code,
@@ -103,14 +103,6 @@
return html`<div id="container">${nodes}</div>`;
}
- constructor() {
- super();
-
- if (this.noTrailingMargin) {
- this.classList.add('noTrailingMargin');
- }
- }
-
/**
* Given a source string, parse into an array of block objects. Each block
* has a `type` property which takes any of the following values.
diff --git a/proto/cache.proto b/proto/cache.proto
index 781538a..aa04555 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -78,7 +78,7 @@
// Instead, we just take the tedious yet simple approach of having a "has_foo"
// field for each nullable field "foo", indicating whether or not foo is null.
//
-// Next ID: 27
+// Next ID: 28
message ChangeNotesStateProto {
// Effectively required, even though the corresponding ChangeNotesState field
// is optional, since the field is only absent when NoteDb is disabled, in
@@ -226,6 +226,8 @@
// Epoch millis.
int64 merged_on_millis = 25;
bool has_merged_on = 26;
+
+ repeated SubmitRequirementResultProto submit_requirement_result = 27;
}
// Serialized form of com.google.gerrit.server.query.change.ConflictKey
@@ -480,6 +482,28 @@
bool allow_override_in_child_projects = 6;
}
+// Serialized form of com.google.gerrit.entities.SubmitRequirementResult.
+// Next ID: 6
+message SubmitRequirementResultProto {
+ SubmitRequirementProto submit_requirement = 1;
+ SubmitRequirementExpressionResultProto applicability_expression_result = 2;
+ SubmitRequirementExpressionResultProto submittability_expression_result = 3;
+ SubmitRequirementExpressionResultProto override_expression_result = 4;
+
+ // Patchset commit ID at which the submit requirements are evaluated.
+ bytes commit = 5;
+}
+
+// Serialized form of com.google.gerrit.entities.SubmitRequirementExpressionResult.
+// Next ID: 6
+message SubmitRequirementExpressionResultProto {
+ string expression = 1;
+ string status = 2; // enum as string
+ string error_message = 3;
+ repeated string passing_atoms = 4;
+ repeated string failing_atoms = 5;
+}
+
// Serialized form of com.google.gerrit.server.project.ConfiguredMimeTypes.
// Next ID: 4
message ConfiguredMimeTypeProto {