Merge "Fix ba-linkify on emails with url-like names"
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index ca105f6..cfee970 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -1282,7 +1282,7 @@
}
protected void assertNotifyCc(TestAccount expected) {
- assertNotifyCc(expected.getEmailAddress());
+ assertNotifyCc(expected.getNameEmail());
}
protected void assertNotifyCc(String expectedEmail, String expectedFullname) {
@@ -1302,7 +1302,7 @@
protected void assertNotifyBcc(TestAccount expected) {
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(expected.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(expected.getNameEmail());
assertThat(m.headers().get("To").isEmpty()).isTrue();
assertThat(m.headers().get("Cc").isEmpty()).isTrue();
}
diff --git a/java/com/google/gerrit/acceptance/TestAccount.java b/java/com/google/gerrit/acceptance/TestAccount.java
index 07bb739..8b39c9e 100644
--- a/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/java/com/google/gerrit/acceptance/TestAccount.java
@@ -79,7 +79,7 @@
.toString();
}
- public Address getEmailAddress() {
+ public Address getNameEmail() {
// Address is weird enough that it's safer and clearer to create a new instance in a
// non-abstract method rather than, say, having an abstract emailAddress() as part of this
// AutoValue class. Specifically:
diff --git a/java/com/google/gerrit/entities/AttentionStatus.java b/java/com/google/gerrit/entities/AttentionStatus.java
new file mode 100644
index 0000000..c488ccd
--- /dev/null
+++ b/java/com/google/gerrit/entities/AttentionStatus.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2020 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.entities;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.common.Nullable;
+import java.time.Instant;
+
+/**
+ * A single update to the attention set. To reconstruct the attention set these instances are parsed
+ * in reverse chronological order. Since each update contains all required information and
+ * invalidates all previous state (hence the name -Status rather than -Update), only the most recent
+ * record is relevant for each user.
+ *
+ * <p>See <a href="https://www.gerritcodereview.com/design-docs/attention-set.html">here</a> for
+ * details.
+ */
+@AutoValue
+public abstract class AttentionStatus {
+
+ /** Users can be added to or removed from the attention set. */
+ public enum Operation {
+ ADD,
+ REMOVE
+ }
+
+ /**
+ * The time at which this status was set. This is null for instances to be written because the
+ * timestamp in the commit message will be used.
+ */
+ @Nullable
+ public abstract Instant timestamp();
+
+ /** The user included in or excluded from the attention set. */
+ public abstract Account.Id account();
+
+ /** Indicates whether the user is added to or removed from the attention set. */
+ public abstract Operation operation();
+
+ /** A short human readable reason that explains this status (e.g. "manual"). */
+ public abstract String reason();
+
+ /**
+ * Create an instance from data read from NoteDB. This includes the timestamp taken from the
+ * commit.
+ */
+ public static AttentionStatus createFromRead(
+ Instant timestamp, Account.Id account, Operation operation, String reason) {
+ return new AutoValue_AttentionStatus(timestamp, account, operation, reason);
+ }
+
+ /**
+ * Create an instance to be written to NoteDB. This has no timestamp because the timestamp of the
+ * commit will be used.
+ */
+ public static AttentionStatus createForWrite(
+ Account.Id account, Operation operation, String reason) {
+ return new AutoValue_AttentionStatus(null, account, operation, reason);
+ }
+}
diff --git a/java/com/google/gerrit/mail/Address.java b/java/com/google/gerrit/mail/Address.java
index 24ab353..455cc90 100644
--- a/java/com/google/gerrit/mail/Address.java
+++ b/java/com/google/gerrit/mail/Address.java
@@ -16,6 +16,7 @@
import com.google.gerrit.common.Nullable;
+/** Represents an address (name + email) in an email message. */
public class Address {
public static Address parse(String in) {
final int lt = in.indexOf('<');
diff --git a/java/com/google/gerrit/pgm/http/jetty/BUILD b/java/com/google/gerrit/pgm/http/jetty/BUILD
index 6ceb242..cd188f5 100644
--- a/java/com/google/gerrit/pgm/http/jetty/BUILD
+++ b/java/com/google/gerrit/pgm/http/jetty/BUILD
@@ -10,10 +10,10 @@
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/server",
- "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/sshd",
"//java/com/google/gerrit/util/http",
+ "//java/com/google/gerrit/util/logging",
"//lib:gson",
"//lib:guava",
"//lib:jgit",
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java b/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
index 33916ac..73d9ee4 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
@@ -24,8 +24,8 @@
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_USER;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_USER_AGENT;
-import com.google.gerrit.server.logging.JsonLayout;
-import com.google.gerrit.server.logging.JsonLogEntry;
+import com.google.gerrit.util.logging.JsonLayout;
+import com.google.gerrit.util.logging.JsonLogEntry;
import java.time.format.DateTimeFormatter;
import org.apache.log4j.spi.LoggingEvent;
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 39fd103..89a2036 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -21,7 +21,6 @@
import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Comparator.naturalOrder;
-import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Strings;
@@ -44,8 +43,6 @@
import com.google.gerrit.exceptions.MergeWithConflictsNotSupportedException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -62,7 +59,6 @@
import com.google.gerrit.server.submit.IntegrationException;
import com.google.gerrit.server.submit.MergeIdenticalTreeException;
import com.google.gerrit.server.submit.MergeSorter;
-import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -128,44 +124,6 @@
*/
private static final int NAME_ABBREV_LEN = 6;
- static class PluggableCommitMessageGenerator {
- private final DynamicSet<ChangeMessageModifier> changeMessageModifiers;
-
- @Inject
- PluggableCommitMessageGenerator(DynamicSet<ChangeMessageModifier> changeMessageModifiers) {
- this.changeMessageModifiers = changeMessageModifiers;
- }
-
- public String generate(
- RevCommit original, RevCommit mergeTip, BranchNameKey dest, String originalMessage) {
- requireNonNull(original.getRawBuffer());
- if (mergeTip != null) {
- requireNonNull(mergeTip.getRawBuffer());
- }
-
- int count = 0;
- String current = originalMessage;
- for (Extension<ChangeMessageModifier> ext : changeMessageModifiers.entries()) {
- ChangeMessageModifier changeMessageModifier = ext.get();
- String className = changeMessageModifier.getClass().getName();
- current = changeMessageModifier.onSubmit(current, original, mergeTip, dest);
- checkState(
- current != null,
- "%s.onSubmit from plugin %s returned null instead of new commit message",
- className,
- ext.getPluginName());
- count++;
- logger.atFine().log(
- "Invoked %s from plugin %s, message length now %d",
- className, ext.getPluginName(), current.length());
- }
- logger.atFine().log(
- "Invoked %d ChangeMessageModifiers on message with original length %d",
- count, originalMessage.length());
- return current;
- }
- }
-
private static final String R_HEADS_MASTER = Constants.R_HEADS + Constants.MASTER;
public static boolean useRecursiveMerge(Config cfg) {
@@ -432,7 +390,33 @@
RevCommit originalCommit,
String mergeStrategy,
boolean allowConflicts,
- PersonIdent committerIndent,
+ PersonIdent committerIdent,
+ String commitMsg,
+ CodeReviewRevWalk rw)
+ throws IOException, MergeIdenticalTreeException, MergeConflictException,
+ InvalidMergeStrategyException {
+ return createMergeCommit(
+ inserter,
+ repoConfig,
+ mergeTip,
+ originalCommit,
+ mergeStrategy,
+ allowConflicts,
+ committerIdent,
+ committerIdent,
+ commitMsg,
+ rw);
+ }
+
+ public static CodeReviewCommit createMergeCommit(
+ ObjectInserter inserter,
+ Config repoConfig,
+ RevCommit mergeTip,
+ RevCommit originalCommit,
+ String mergeStrategy,
+ boolean allowConflicts,
+ PersonIdent authorIdent,
+ PersonIdent committerIdent,
String commitMsg,
CodeReviewRevWalk rw)
throws IOException, MergeIdenticalTreeException, MergeConflictException,
@@ -496,8 +480,8 @@
CommitBuilder mergeCommit = new CommitBuilder();
mergeCommit.setTreeId(tree);
mergeCommit.setParentIds(mergeTip, originalCommit);
- mergeCommit.setAuthor(committerIndent);
- mergeCommit.setCommitter(committerIndent);
+ mergeCommit.setAuthor(authorIdent);
+ mergeCommit.setCommitter(committerIdent);
mergeCommit.setMessage(commitMsg);
CodeReviewCommit commit = rw.parseCommit(inserter.insert(mergeCommit));
commit.setFilesWithGitConflicts(filesWithGitConflicts);
diff --git a/java/com/google/gerrit/server/git/PluggableCommitMessageGenerator.java b/java/com/google/gerrit/server/git/PluggableCommitMessageGenerator.java
new file mode 100644
index 0000000..804a218
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PluggableCommitMessageGenerator.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2020 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.git;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.Extension;
+import com.google.inject.Inject;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+/** Helper to call plugins that want to change the commit message before a change is merged. */
+public class PluggableCommitMessageGenerator {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final DynamicSet<ChangeMessageModifier> changeMessageModifiers;
+
+ @Inject
+ PluggableCommitMessageGenerator(DynamicSet<ChangeMessageModifier> changeMessageModifiers) {
+ this.changeMessageModifiers = changeMessageModifiers;
+ }
+
+ /**
+ * Returns the commit message as modified by plugins. The returned message can be equal to {@code
+ * originalMessage} in case no plugins are registered or the registered plugins decided not to
+ * modify the message.
+ */
+ public String generate(
+ RevCommit original, RevCommit mergeTip, BranchNameKey dest, String originalMessage) {
+ requireNonNull(original.getRawBuffer());
+ if (mergeTip != null) {
+ requireNonNull(mergeTip.getRawBuffer());
+ }
+
+ int count = 0;
+ String current = originalMessage;
+ for (Extension<ChangeMessageModifier> ext : changeMessageModifiers.entries()) {
+ ChangeMessageModifier changeMessageModifier = ext.get();
+ String className = changeMessageModifier.getClass().getName();
+ current = changeMessageModifier.onSubmit(current, original, mergeTip, dest);
+ checkState(
+ current != null,
+ "%s.onSubmit from plugin %s returned null instead of new commit message",
+ className,
+ ext.getPluginName());
+ count++;
+ logger.atFine().log(
+ "Invoked %s from plugin %s, message length now %d",
+ className, ext.getPluginName(), current.length());
+ }
+ logger.atFine().log(
+ "Invoked %d ChangeMessageModifiers on message with original length %d",
+ count, originalMessage.length());
+ return current;
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
index 1f0c58a..7136d2b 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
@@ -110,7 +110,7 @@
ChangeNoteUtil noteUtil, PersonIdent serverIdent, CurrentUser u, Date when) {
checkUserType(u);
if (u instanceof IdentifiedUser) {
- return noteUtil.newIdent(u.asIdentifiedUser().getAccount(), when, serverIdent);
+ return noteUtil.newIdent(u.asIdentifiedUser().getAccount().id(), when, serverIdent);
} else if (u instanceof InternalUser) {
return serverIdent;
}
@@ -164,10 +164,6 @@
return accountId;
}
- protected PersonIdent newIdent(Account.Id authorId, Date when) {
- return noteUtil.newIdent(authorId, when, serverIdent);
- }
-
/** Whether no updates have been done. */
public abstract boolean isEmpty();
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index cebb67d..287f3e7 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -15,10 +15,13 @@
package com.google.gerrit.server.notedb;
import com.google.auto.value.AutoValue;
-import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
+import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.server.config.GerritServerId;
+import com.google.gson.Gson;
import com.google.inject.Inject;
+import java.time.Instant;
import java.util.Date;
import java.util.Optional;
import org.eclipse.jgit.lib.PersonIdent;
@@ -27,42 +30,31 @@
import org.eclipse.jgit.util.RawParseUtils;
public class ChangeNoteUtil {
- public static final FooterKey FOOTER_ASSIGNEE = new FooterKey("Assignee");
- public static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
- public static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
- public static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
- public static final FooterKey FOOTER_CURRENT = new FooterKey("Current");
- public static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
- public static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
- public static final FooterKey FOOTER_LABEL = new FooterKey("Label");
- public static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
- public static final FooterKey FOOTER_PATCH_SET_DESCRIPTION =
- new FooterKey("Patch-set-description");
- public static final FooterKey FOOTER_PRIVATE = new FooterKey("Private");
- public static final FooterKey FOOTER_REAL_USER = new FooterKey("Real-user");
- public static final FooterKey FOOTER_STATUS = new FooterKey("Status");
- public static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
- public static final FooterKey FOOTER_SUBMISSION_ID = new FooterKey("Submission-id");
- public static final FooterKey FOOTER_SUBMITTED_WITH = new FooterKey("Submitted-with");
- public static final FooterKey FOOTER_TOPIC = new FooterKey("Topic");
- public static final FooterKey FOOTER_TAG = new FooterKey("Tag");
- public static final FooterKey FOOTER_WORK_IN_PROGRESS = new FooterKey("Work-in-progress");
- public static final FooterKey FOOTER_REVERT_OF = new FooterKey("Revert-of");
- public static final FooterKey FOOTER_CHERRY_PICK_OF = new FooterKey("Cherry-pick-of");
- static final String AUTHOR = "Author";
- static final String BASE_PATCH_SET = "Base-for-patch-set";
- static final String COMMENT_RANGE = "Comment-range";
- static final String FILE = "File";
- static final String LENGTH = "Bytes";
- static final String PARENT = "Parent";
- static final String PARENT_NUMBER = "Parent-number";
- static final String PATCH_SET = "Patch-set";
- static final String REAL_AUTHOR = "Real-author";
- static final String REVISION = "Revision";
- static final String UUID = "UUID";
- static final String UNRESOLVED = "Unresolved";
- static final String TAG = FOOTER_TAG.getName();
+ static final FooterKey FOOTER_ATTENTION = new FooterKey("Attention");
+ static final FooterKey FOOTER_ASSIGNEE = new FooterKey("Assignee");
+ static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
+ static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
+ static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
+ static final FooterKey FOOTER_CURRENT = new FooterKey("Current");
+ static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
+ static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
+ static final FooterKey FOOTER_LABEL = new FooterKey("Label");
+ static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
+ static final FooterKey FOOTER_PATCH_SET_DESCRIPTION = new FooterKey("Patch-set-description");
+ static final FooterKey FOOTER_PRIVATE = new FooterKey("Private");
+ static final FooterKey FOOTER_REAL_USER = new FooterKey("Real-user");
+ static final FooterKey FOOTER_STATUS = new FooterKey("Status");
+ static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
+ static final FooterKey FOOTER_SUBMISSION_ID = new FooterKey("Submission-id");
+ static final FooterKey FOOTER_SUBMITTED_WITH = new FooterKey("Submitted-with");
+ static final FooterKey FOOTER_TOPIC = new FooterKey("Topic");
+ static final FooterKey FOOTER_TAG = new FooterKey("Tag");
+ static final FooterKey FOOTER_WORK_IN_PROGRESS = new FooterKey("Work-in-progress");
+ static final FooterKey FOOTER_REVERT_OF = new FooterKey("Revert-of");
+ static final FooterKey FOOTER_CHERRY_PICK_OF = new FooterKey("Cherry-pick-of");
+
+ private static final Gson gson = OutputFormat.JSON_COMPACT.newGson();
private final ChangeNoteJson changeNoteJson;
private final String serverId;
@@ -77,21 +69,17 @@
return changeNoteJson;
}
- public PersonIdent newIdent(Account.Id authorId, Date when, PersonIdent serverIdent) {
+ public PersonIdent newIdent(Account.Id accountId, Date when, PersonIdent serverIdent) {
return new PersonIdent(
- "Gerrit User " + authorId.toString(),
- authorId.get() + "@" + serverId,
- when,
- serverIdent.getTimeZone());
+ getUsername(accountId), getEmailAddress(accountId), when, serverIdent.getTimeZone());
}
- @VisibleForTesting
- public PersonIdent newIdent(Account author, Date when, PersonIdent serverIdent) {
- return new PersonIdent(
- "Gerrit User " + author.id(),
- author.id().get() + "@" + serverId,
- when,
- serverIdent.getTimeZone());
+ private static String getUsername(Account.Id accountId) {
+ return "Gerrit User " + accountId.toString();
+ }
+
+ private String getEmailAddress(Account.Id accountId) {
+ return accountId.get() + "@" + serverId;
}
public static Optional<CommitMessageRange> parseCommitMessageRange(RevCommit commit) {
@@ -151,6 +139,7 @@
@AutoValue
public abstract static class CommitMessageRange {
+
public abstract int subjectStart();
public abstract int subjectEnd();
@@ -165,6 +154,7 @@
@AutoValue.Builder
public abstract static class Builder {
+
abstract Builder subjectStart(int subjectStart);
abstract Builder subjectEnd(int subjectEnd);
@@ -176,4 +166,51 @@
abstract CommitMessageRange build();
}
}
+
+ /** Helper class for JSON serialization. Timestamp is taken from the commit. */
+ private static class AttentionStatusInNoteDb {
+
+ final String personIdent;
+ final AttentionStatus.Operation operation;
+ final String reason;
+
+ AttentionStatusInNoteDb(
+ String personIndent, AttentionStatus.Operation operation, String reason) {
+ this.personIdent = personIndent;
+ this.operation = operation;
+ this.reason = reason;
+ }
+ }
+
+ /** The returned {@link Optional} holds the parsed entity or is empty if parsing failed. */
+ static Optional<AttentionStatus> attentionStatusFromJson(
+ Instant timestamp, String attentionString) {
+ AttentionStatusInNoteDb inNoteDb =
+ gson.fromJson(attentionString, AttentionStatusInNoteDb.class);
+ PersonIdent personIdent = RawParseUtils.parsePersonIdent(inNoteDb.personIdent);
+ if (personIdent == null) {
+ return Optional.empty();
+ }
+ Optional<Account.Id> account = NoteDbUtil.parseIdent(personIdent);
+ return account.map(
+ id -> AttentionStatus.createFromRead(timestamp, id, inNoteDb.operation, inNoteDb.reason));
+ }
+
+ String attentionStatusToJson(AttentionStatus attentionStatus) {
+ PersonIdent personIdent =
+ new PersonIdent(
+ getUsername(attentionStatus.account()), getEmailAddress(attentionStatus.account()));
+ StringBuilder stringBuilder = new StringBuilder();
+ appendIdentString(stringBuilder, personIdent.getName(), personIdent.getEmailAddress());
+ return gson.toJson(
+ new AttentionStatusInNoteDb(
+ stringBuilder.toString(), attentionStatus.operation(), attentionStatus.reason()));
+ }
+
+ static void appendIdentString(StringBuilder stringBuilder, String name, String emailAddress) {
+ PersonIdent.appendSanitized(stringBuilder, name);
+ stringBuilder.append(" <");
+ PersonIdent.appendSanitized(stringBuilder, emailAddress);
+ stringBuilder.append('>');
+ }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index b7ce2a8..2de2195 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -40,6 +40,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
@@ -375,6 +376,10 @@
return state.reviewerUpdates();
}
+ public ImmutableList<AttentionStatus> getAttentionUpdates() {
+ return state.attentionUpdates();
+ }
+
/**
* @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 428df16..ed6039f 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
+import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ATTENTION;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHERRY_PICK_OF;
@@ -43,6 +44,7 @@
import com.google.common.base.Enums;
import com.google.common.base.Splitter;
import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.ListMultimap;
@@ -57,6 +59,7 @@
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
@@ -75,6 +78,7 @@
import java.io.IOException;
import java.nio.charset.Charset;
import java.sql.Timestamp;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -102,7 +106,6 @@
// Private final members initialized in the constructor.
private final ChangeNoteJson changeNoteJson;
-
private final NoteDbMetrics metrics;
private final Change.Id id;
private final ObjectId tip;
@@ -114,6 +117,9 @@
private final Table<Address, ReviewerStateInternal, Timestamp> reviewersByEmail;
private final List<Account.Id> allPastReviewers;
private final List<ReviewerStatusUpdate> reviewerUpdates;
+ /** Holds only the most recent update per user. Older updates are discarded. */
+ private final Map<Account.Id, AttentionStatus> latestAttentionStatus;
+
private final List<AssigneeStatusUpdate> assigneeUpdates;
private final List<SubmitRecord> submitRecords;
private final ListMultimap<ObjectId, Comment> comments;
@@ -169,6 +175,7 @@
pendingReviewersByEmail = ReviewerByEmailSet.empty();
allPastReviewers = new ArrayList<>();
reviewerUpdates = new ArrayList<>();
+ latestAttentionStatus = new HashMap<>();
assigneeUpdates = new ArrayList<>();
submitRecords = Lists.newArrayListWithExpectedSize(1);
allChangeMessages = new ArrayList<>();
@@ -239,6 +246,7 @@
pendingReviewersByEmail,
allPastReviewers,
buildReviewerUpdates(),
+ ImmutableList.copyOf(latestAttentionStatus.values()),
assigneeUpdates,
submitRecords,
buildAllMessages(),
@@ -359,6 +367,7 @@
}
parseHashtags(commit);
+ parseAttentionUpdates(commit);
parseAssigneeUpdates(ts, commit);
if (submissionId == null) {
@@ -569,6 +578,21 @@
}
}
+ private void parseAttentionUpdates(ChangeNotesCommit commit) throws ConfigInvalidException {
+ List<String> attentionStrings = commit.getFooterLineValues(FOOTER_ATTENTION);
+ for (String attentionString : attentionStrings) {
+
+ Optional<AttentionStatus> attentionStatus =
+ ChangeNoteUtil.attentionStatusFromJson(
+ Instant.ofEpochSecond(commit.getCommitTime()), attentionString);
+ if (!attentionStatus.isPresent()) {
+ throw invalidFooter(FOOTER_ATTENTION, attentionString);
+ }
+ // Processing is in reverse chronological order. Keep only the latest update.
+ latestAttentionStatus.putIfAbsent(attentionStatus.get().account(), attentionStatus.get());
+ }
+ }
+
private void parseAssigneeUpdates(Timestamp ts, ChangeNotesCommit commit)
throws ConfigInvalidException {
String assigneeValue = parseOneFooter(commit, FOOTER_ASSIGNEE);
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 064e43b..1931e7e 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -35,6 +35,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
@@ -55,6 +56,7 @@
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AssigneeStatusUpdateProto;
+import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AttentionStatusProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
@@ -66,6 +68,7 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.MessageLite;
import java.sql.Timestamp;
+import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -83,11 +86,12 @@
*/
@AutoValue
public abstract class ChangeNotesState {
+
static ChangeNotesState empty(Change change) {
return Builder.empty(change.getId()).build();
}
- static Builder builder() {
+ private static Builder builder() {
return new AutoValue_ChangeNotesState.Builder();
}
@@ -115,6 +119,7 @@
ReviewerByEmailSet pendingReviewersByEmail,
List<Account.Id> allPastReviewers,
List<ReviewerStatusUpdate> reviewerUpdates,
+ List<AttentionStatus> attentionStatusUpdates,
List<AssigneeStatusUpdate> assigneeUpdates,
List<SubmitRecord> submitRecords,
List<ChangeMessage> changeMessages,
@@ -165,6 +170,7 @@
.pendingReviewersByEmail(pendingReviewersByEmail)
.allPastReviewers(allPastReviewers)
.reviewerUpdates(reviewerUpdates)
+ .attentionUpdates(attentionStatusUpdates)
.assigneeUpdates(assigneeUpdates)
.submitRecords(submitRecords)
.changeMessages(changeMessages)
@@ -180,6 +186,7 @@
*/
@AutoValue
abstract static class ChangeColumns {
+
static Builder builder() {
return new AutoValue_ChangeNotesState_ChangeColumns.Builder();
}
@@ -229,6 +236,7 @@
@AutoValue.Builder
abstract static class Builder {
+
abstract Builder changeKey(Change.Key changeKey);
abstract Builder createdOn(Timestamp createdOn);
@@ -297,6 +305,8 @@
abstract ImmutableList<ReviewerStatusUpdate> reviewerUpdates();
+ abstract ImmutableList<AttentionStatus> attentionUpdates();
+
abstract ImmutableList<AssigneeStatusUpdate> assigneeUpdates();
abstract ImmutableList<SubmitRecord> submitRecords();
@@ -361,6 +371,7 @@
@AutoValue.Builder
abstract static class Builder {
+
static Builder empty(Change.Id changeId) {
return new AutoValue_ChangeNotesState.Builder()
.changeId(changeId)
@@ -373,6 +384,7 @@
.pendingReviewersByEmail(ReviewerByEmailSet.empty())
.allPastReviewers(ImmutableList.of())
.reviewerUpdates(ImmutableList.of())
+ .attentionUpdates(ImmutableList.of())
.assigneeUpdates(ImmutableList.of())
.submitRecords(ImmutableList.of())
.changeMessages(ImmutableList.of())
@@ -406,6 +418,8 @@
abstract Builder reviewerUpdates(List<ReviewerStatusUpdate> reviewerUpdates);
+ abstract Builder attentionUpdates(List<AttentionStatus> attentionUpdates);
+
abstract Builder assigneeUpdates(List<AssigneeStatusUpdate> assigneeUpdates);
abstract Builder submitRecords(List<SubmitRecord> submitRecords);
@@ -473,6 +487,7 @@
object.allPastReviewers().forEach(a -> b.addPastReviewer(a.get()));
object.reviewerUpdates().forEach(u -> b.addReviewerUpdate(toReviewerStatusUpdateProto(u)));
+ object.attentionUpdates().forEach(u -> b.addAttentionStatus(toAttentionStatusProto(u)));
object.assigneeUpdates().forEach(u -> b.addAssigneeUpdate(toAssigneeStatusUpdateProto(u)));
object
.submitRecords()
@@ -556,6 +571,15 @@
.build();
}
+ private static AttentionStatusProto toAttentionStatusProto(AttentionStatus attentionStatus) {
+ return AttentionStatusProto.newBuilder()
+ .setDate(attentionStatus.timestamp().toEpochMilli())
+ .setAccount(attentionStatus.account().get())
+ .setOperation(attentionStatus.operation().name())
+ .setReason(attentionStatus.reason())
+ .build();
+ }
+
private static AssigneeStatusUpdateProto toAssigneeStatusUpdateProto(AssigneeStatusUpdate u) {
AssigneeStatusUpdateProto.Builder builder =
AssigneeStatusUpdateProto.newBuilder()
@@ -596,6 +620,7 @@
.allPastReviewers(
proto.getPastReviewerList().stream().map(Account::id).collect(toImmutableList()))
.reviewerUpdates(toReviewerStatusUpdateList(proto.getReviewerUpdateList()))
+ .attentionUpdates(toAttentionUpdateList(proto.getAttentionStatusList()))
.assigneeUpdates(toAssigneeStatusUpdateList(proto.getAssigneeUpdateList()))
.submitRecords(
proto.getSubmitRecordList().stream()
@@ -694,6 +719,20 @@
return b.build();
}
+ private static ImmutableList<AttentionStatus> toAttentionUpdateList(
+ List<AttentionStatusProto> protos) {
+ ImmutableList.Builder<AttentionStatus> b = ImmutableList.builder();
+ for (AttentionStatusProto proto : protos) {
+ b.add(
+ AttentionStatus.createFromRead(
+ Instant.ofEpochMilli(proto.getDate()),
+ Account.id(proto.getAccount()),
+ AttentionStatus.Operation.valueOf(proto.getOperation()),
+ proto.getReason()));
+ }
+ return b.build();
+ }
+
private static ImmutableList<AssigneeStatusUpdate> toAssigneeStatusUpdateList(
List<AssigneeStatusUpdateProto> protos) {
ImmutableList.Builder<AssigneeStatusUpdate> b = ImmutableList.builder();
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 2822f61..3ef631b 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -19,6 +19,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.entities.RefNames.changeMetaRef;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
+import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ATTENTION;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHERRY_PICK_OF;
@@ -52,6 +53,7 @@
import com.google.common.collect.TreeBasedTable;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.Project;
@@ -125,6 +127,7 @@
private String submissionId;
private String topic;
private String commit;
+ private List<AttentionStatus> attentionUpdates;
private Optional<Account.Id> assignee;
private Set<String> hashtags;
private String changeMessage;
@@ -368,6 +371,21 @@
this.hashtags = hashtags;
}
+ /**
+ * All updates must have a timestamp of null since we use the commit's timestamp. There also must
+ * not be multiple updates for a single user.
+ */
+ void setAttentionUpdates(List<AttentionStatus> attentionUpdates) {
+ checkArgument(
+ attentionUpdates.stream().noneMatch(x -> x.timestamp() != null),
+ "must not specify timestamp for write");
+ checkArgument(
+ attentionUpdates.stream().map(AttentionStatus::account).distinct().count()
+ == attentionUpdates.size(),
+ "must not specify multiple updates for single user");
+ this.attentionUpdates = attentionUpdates;
+ }
+
public void setAssignee(Account.Id assignee) {
checkArgument(assignee != null, "use removeAssignee");
this.assignee = Optional.of(assignee);
@@ -578,6 +596,12 @@
addFooter(msg, FOOTER_COMMIT, commit);
}
+ if (attentionUpdates != null) {
+ for (AttentionStatus attentionUpdate : attentionUpdates) {
+ addFooter(msg, FOOTER_ATTENTION, noteUtil.attentionStatusToJson(attentionUpdate));
+ }
+ }
+
if (assignee != null) {
if (assignee.isPresent()) {
addFooter(msg, FOOTER_ASSIGNEE);
@@ -713,6 +737,7 @@
&& status == null
&& submissionId == null
&& submitRecords == null
+ && attentionUpdates == null
&& assignee == null
&& hashtags == null
&& topic == null
@@ -774,12 +799,8 @@
}
private StringBuilder addIdent(StringBuilder sb, Account.Id accountId) {
- PersonIdent ident = newIdent(accountId, when);
-
- PersonIdent.appendSanitized(sb, ident.getName());
- sb.append(" <");
- PersonIdent.appendSanitized(sb, ident.getEmailAddress());
- sb.append('>');
+ PersonIdent ident = noteUtil.newIdent(accountId, when, serverIdent);
+ ChangeNoteUtil.appendIdentString(sb, ident.getName(), ident.getEmailAddress());
return sb;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index a6a1eef..9e792d0 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -335,9 +335,10 @@
Timestamp now = TimeUtil.nowTs();
+ PersonIdent committer = me.newCommitterIdent(now, serverTimeZone);
PersonIdent author =
input.author == null
- ? me.newCommitterIdent(now, serverTimeZone)
+ ? committer
: new PersonIdent(input.author.name, input.author.email, now, serverTimeZone);
String commitMessage = getCommitMessage(input.subject, me);
@@ -345,7 +346,9 @@
CodeReviewCommit c;
if (input.merge != null) {
// create a merge commit
- c = newMergeCommit(git, oi, rw, projectState, mergeTip, input.merge, author, commitMessage);
+ c =
+ newMergeCommit(
+ git, oi, rw, projectState, mergeTip, input.merge, author, committer, commitMessage);
if (!c.getFilesWithGitConflicts().isEmpty()) {
logger.atFine().log(
"merge commit has conflicts in the following files: %s",
@@ -353,7 +356,7 @@
}
} else {
// create an empty commit
- c = newCommit(oi, rw, author, mergeTip, commitMessage);
+ c = newCommit(oi, rw, author, committer, mergeTip, commitMessage);
}
Change.Id changeId = Change.id(seq.nextChangeId());
@@ -495,6 +498,7 @@
ObjectInserter oi,
CodeReviewRevWalk rw,
PersonIdent authorIdent,
+ PersonIdent committerIdent,
RevCommit mergeTip,
String commitMessage)
throws IOException {
@@ -507,7 +511,7 @@
commit.setParentId(mergeTip);
}
commit.setAuthor(authorIdent);
- commit.setCommitter(authorIdent);
+ commit.setCommitter(committerIdent);
commit.setMessage(commitMessage);
return rw.parseCommit(insert(oi, commit));
}
@@ -520,6 +524,7 @@
RevCommit mergeTip,
MergeInput merge,
PersonIdent authorIdent,
+ PersonIdent committerIdent,
String commitMessage)
throws RestApiException, IOException {
logger.atFine().log(
@@ -556,6 +561,7 @@
mergeStrategy,
merge.allowConflicts,
authorIdent,
+ committerIdent,
commitMessage,
rw);
} catch (NoMergeBaseException e) {
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index f567a3a..a5b88b4 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -22,6 +22,7 @@
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli",
+ "//java/com/google/gerrit/util/logging",
"//lib:args4j",
"//lib:gson",
"//lib:guava",
diff --git a/java/com/google/gerrit/sshd/SshLogJsonLayout.java b/java/com/google/gerrit/sshd/SshLogJsonLayout.java
index 8495ed1..5ece5af 100644
--- a/java/com/google/gerrit/sshd/SshLogJsonLayout.java
+++ b/java/com/google/gerrit/sshd/SshLogJsonLayout.java
@@ -23,8 +23,8 @@
import static com.google.gerrit.sshd.SshLog.P_USER_NAME;
import static com.google.gerrit.sshd.SshLog.P_WAIT;
-import com.google.gerrit.server.logging.JsonLayout;
-import com.google.gerrit.server.logging.JsonLogEntry;
+import com.google.gerrit.util.logging.JsonLayout;
+import com.google.gerrit.util.logging.JsonLogEntry;
import java.time.format.DateTimeFormatter;
import org.apache.log4j.spi.LoggingEvent;
diff --git a/java/com/google/gerrit/util/logging/BUILD b/java/com/google/gerrit/util/logging/BUILD
new file mode 100644
index 0000000..b8db49bd
--- /dev/null
+++ b/java/com/google/gerrit/util/logging/BUILD
@@ -0,0 +1,13 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "logging",
+ srcs = glob(
+ ["*.java"],
+ ),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:gson",
+ "//lib/log:log4j",
+ ],
+)
diff --git a/java/com/google/gerrit/server/logging/JsonLayout.java b/java/com/google/gerrit/util/logging/JsonLayout.java
similarity index 94%
rename from java/com/google/gerrit/server/logging/JsonLayout.java
rename to java/com/google/gerrit/util/logging/JsonLayout.java
index 3eb4515..45fbd32 100644
--- a/java/com/google/gerrit/server/logging/JsonLayout.java
+++ b/java/com/google/gerrit/util/logging/JsonLayout.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.logging;
+package com.google.gerrit.util.logging;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
@@ -34,7 +34,7 @@
public JsonLayout() {
dateFormatter = createDateTimeFormatter();
- timeOffset = OffsetDateTime.now().getOffset();
+ timeOffset = OffsetDateTime.now(ZoneId.systemDefault()).getOffset();
gson = newGson();
}
diff --git a/java/com/google/gerrit/server/logging/JsonLogEntry.java b/java/com/google/gerrit/util/logging/JsonLogEntry.java
similarity index 94%
rename from java/com/google/gerrit/server/logging/JsonLogEntry.java
rename to java/com/google/gerrit/util/logging/JsonLogEntry.java
index bc16c70..ceac427 100644
--- a/java/com/google/gerrit/server/logging/JsonLogEntry.java
+++ b/java/com/google/gerrit/util/logging/JsonLogEntry.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.logging;
+package com.google.gerrit.util.logging;
import org.apache.log4j.spi.LoggingEvent;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 8ef49a1..e2d2501 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -857,7 +857,7 @@
gApi.changes().id(r.getChangeId()).abandon();
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
- assertThat(messages.get(0).rcpt()).containsExactly(user2.getEmailAddress());
+ assertThat(messages.get(0).rcpt()).containsExactly(user2.getNameEmail());
accountIndexedCounter.assertNoReindex();
}
}
@@ -883,7 +883,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message message = messages.get(0);
- assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(message.rcpt()).containsExactly(user.getNameEmail());
assertMailReplyTo(message, admin.email());
accountIndexedCounter.assertNoReindex();
}
@@ -1840,7 +1840,7 @@
assertThat(sender.getMessages()).hasSize(1);
Message message = sender.getMessages().get(0);
- assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(message.rcpt()).containsExactly(user.getNameEmail());
assertThat(message.body()).contains("new SSH keys have been added");
// Delete key
@@ -1852,7 +1852,7 @@
assertThat(sender.getMessages()).hasSize(1);
message = sender.getMessages().get(0);
- assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(message.rcpt()).containsExactly(user.getNameEmail());
assertThat(message.body()).contains("SSH keys have been deleted");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 0eefe02..9399c3b 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -1518,7 +1518,7 @@
assertThat(messages).hasSize(1);
Message m = messages.get(0);
assertThat(m.from().getName()).isEqualTo("Administrator (Code Review)");
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("I'd like you to do a code review");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
assertMailReplyTo(m, admin.email());
@@ -1587,7 +1587,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
assertThat(m.body()).contains("I'd like you to do a code review.");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
@@ -1771,7 +1771,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
assertThat(m.body()).contains("I'd like you to do a code review.");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
@@ -2162,7 +2162,7 @@
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
}
@Test
@@ -2411,7 +2411,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message msg = messages.get(0);
- assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(msg.rcpt()).containsExactly(user.getNameEmail());
assertThat(msg.body()).contains(admin.fullName() + " has removed a vote from this change.");
assertThat(msg.body())
.contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");
@@ -3065,7 +3065,7 @@
assertThat(commitPatchSetCreation.getShortMessage()).isEqualTo("Create patch set 2");
PersonIdent expectedAuthor =
- changeNoteUtil.newIdent(getAccount(admin.id()), c.updated, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id()).id(), c.updated, serverIdent.get());
assertThat(commitPatchSetCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commitPatchSetCreation.getCommitterIdent())
.isEqualTo(new PersonIdent(serverIdent.get(), c.updated));
@@ -3074,7 +3074,7 @@
RevCommit commitChangeCreation = rw.parseCommit(commitPatchSetCreation.getParent(0));
assertThat(commitChangeCreation.getShortMessage()).isEqualTo("Create change");
expectedAuthor =
- changeNoteUtil.newIdent(getAccount(admin.id()), c.created, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id()).id(), c.created, serverIdent.get());
assertThat(commitChangeCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commitChangeCreation.getCommitterIdent())
.isEqualTo(new PersonIdent(serverIdent.get(), c.created));
@@ -4527,7 +4527,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
- assertThat(messages.get(0).rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(messages.get(0).rcpt()).containsExactly(user.getNameEmail());
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 51dee72..4ee1f35 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -545,7 +545,7 @@
r.assertOkStatus();
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
sender.clear();
r = pushTo(pushSpec + ",notify=" + NotifyHandling.ALL);
@@ -553,7 +553,7 @@
assertThat(sender.getMessages()).hasSize(1);
m = sender.getMessages().get(0);
assertThat(m.rcpt())
- .containsExactly(user.getEmailAddress(), user2.getEmailAddress(), user3.getEmailAddress());
+ .containsExactly(user.getNameEmail(), user2.getNameEmail(), user3.getNameEmail());
sender.clear();
r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + user3.email());
@@ -1116,7 +1116,7 @@
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).rcpt())
- .containsExactly(user.getEmailAddress(), user2.getEmailAddress());
+ .containsExactly(user.getNameEmail(), user2.getNameEmail());
}
@Test
@@ -1141,7 +1141,7 @@
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).rcpt())
- .containsExactly(user.getEmailAddress(), user2.getEmailAddress());
+ .containsExactly(user.getNameEmail(), user2.getNameEmail());
}
/**
@@ -1838,7 +1838,7 @@
@Test
public void pushWithEmailInFooter() throws Exception {
- pushWithReviewerInFooter(user.getEmailAddress().toString(), user);
+ pushWithReviewerInFooter(user.getNameEmail().toString(), user);
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 56d9ff2..1083377 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -1071,7 +1071,7 @@
PersonIdent committer = serverIdent.get();
PersonIdent author =
- noteUtil.newIdent(getAccount(admin.id()), committer.getWhen(), committer);
+ noteUtil.newIdent(getAccount(admin.id()).id(), committer.getWhen(), committer);
tr.branch(RefNames.changeMetaRef(cd3.getId()))
.commit()
.author(author)
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
index 0f5def6..420ddda 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -61,7 +61,7 @@
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index ac00e38..090146e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -209,7 +209,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
assertThat(messages.get(0).rcpt())
- .containsExactly(Address.parse(addInput.reviewer), user.getEmailAddress());
+ .containsExactly(Address.parse(addInput.reviewer), user.getNameEmail());
sender.clear();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index fbe6533..0a006fa 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -150,7 +150,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains(admin.fullName() + " has uploaded this change for review.");
}
@@ -188,7 +188,7 @@
Message m = messages.get(0);
List<Address> expectedAddresses = new ArrayList<>(firstUsers.size());
for (TestAccount u : firstUsers) {
- expectedAddresses.add(u.getEmailAddress());
+ expectedAddresses.add(u.getNameEmail());
}
assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
@@ -216,9 +216,9 @@
m = messages.get(0);
expectedAddresses = new ArrayList<>(4);
for (int i = 0; i < 3; i++) {
- expectedAddresses.add(users.get(users.size() - i - 1).getEmailAddress());
+ expectedAddresses.add(users.get(users.size() - i - 1).getNameEmail());
}
- expectedAddresses.add(reviewer.getEmailAddress());
+ expectedAddresses.add(reviewer.getNameEmail());
assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
}
@@ -398,13 +398,13 @@
assertThat(messages).hasSize(2);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress(), observer.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail(), observer.getNameEmail());
assertThat(m.body()).contains(admin.fullName() + " has posted comments on this change.");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
assertThat(m.body()).contains("Patch Set 1: Code-Review+2");
m = messages.get(1);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress(), observer.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail(), observer.getNameEmail());
assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
assertThat(m.body()).contains("I'd like you to do a code review.");
}
@@ -635,7 +635,7 @@
sender.clear();
gApi.changes().id(r.getChangeId()).current().review(reviewInput);
assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getEmailAddress());
+ assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getNameEmail());
}
@Test
@@ -652,7 +652,7 @@
sender.clear();
gApi.changes().id(r.getChangeId()).addReviewer(addReviewer);
assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getEmailAddress());
+ assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getNameEmail());
}
@Test
@@ -829,7 +829,7 @@
}
private void assertThatUserIsOnlyReviewer(String changeId) throws Exception {
- AccountInfo userInfo = new AccountInfo(user.fullName(), user.getEmailAddress().getEmail());
+ AccountInfo userInfo = new AccountInfo(user.fullName(), user.getNameEmail().getEmail());
userInfo._accountId = user.id().get();
userInfo.username = user.username();
assertThat(gApi.changes().id(changeId).get().reviewers)
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 0af1295..d831b62 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -216,7 +216,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains(admin.fullName() + " has uploaded this change for review.");
// check that watcher is not notified if notify=NONE
@@ -299,9 +299,11 @@
ChangeInfo info = assertCreateSucceeds(input);
RevisionApi rApi = gApi.changes().id(info.id).current();
- GitPerson person = rApi.commit(false).author;
- assertThat(person).email().isEqualTo(input.author.email);
- assertThat(person).name().isEqualTo(input.author.name);
+ GitPerson author = rApi.commit(false).author;
+ assertThat(author).email().isEqualTo(input.author.email);
+ assertThat(author).name().isEqualTo(input.author.name);
+ GitPerson committer = rApi.commit(false).committer;
+ assertThat(committer).email().isEqualTo(admin.getNameEmail().getEmail());
}
@Test
@@ -331,7 +333,19 @@
changeInTwoBranches("foo", "foo.txt", "bar", "bar.txt");
ChangeInput input = newChangeInput(ChangeStatus.NEW);
input.baseCommit = setup.get("master").getCommit().getId().name();
- assertCreateSucceeds(input);
+ ChangeInfo result = assertCreateSucceeds(input);
+ assertThat(gApi.changes().id(result.id).current().commit(false).parents.get(0).commit)
+ .isEqualTo(input.baseCommit);
+ }
+
+ @Test
+ public void createChangeWithParentChange() throws Exception {
+ Result change = createChange();
+ ChangeInput input = newChangeInput(ChangeStatus.NEW);
+ input.baseChange = change.getChangeId();
+ ChangeInfo result = assertCreateSucceeds(input);
+ assertThat(gApi.changes().id(result.id).current().commit(false).parents.get(0).commit)
+ .isEqualTo(change.getCommit().getId().name());
}
@Test
@@ -415,7 +429,7 @@
assertThat(commit.getShortMessage()).isEqualTo("Create change");
PersonIdent expectedAuthor =
- changeNoteUtil.newIdent(getAccount(admin.id()), c.created, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id()).id(), c.created, serverIdent.get());
assertThat(commit.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commit.getCommitterIdent())
@@ -437,6 +451,22 @@
}
@Test
+ public void createMergeChangeAuthor() throws Exception {
+ changeInTwoBranches("branchA", "a.txt", "branchB", "b.txt");
+ ChangeInput in = newMergeChangeInput("branchA", "branchB", "");
+ in.author = new AccountInput();
+ in.author.name = "Gerritless Jane";
+ in.author.email = "gerritlessjane@invalid";
+ ChangeInfo change = assertCreateSucceeds(in);
+
+ RevisionApi rApi = gApi.changes().id(change.id).current();
+ GitPerson author = rApi.commit(false).author;
+ assertThat(author).email().isEqualTo(in.author.email);
+ GitPerson committer = rApi.commit(false).committer;
+ assertThat(committer).email().isEqualTo(admin.getNameEmail().getEmail());
+ }
+
+ @Test
public void createMergeChange_Conflicts() throws Exception {
changeInTwoBranches("branchA", "shared.txt", "branchB", "shared.txt");
ChangeInput in = newMergeChangeInput("branchA", "branchB", "");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
index 37fa2ce..25e5647 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
@@ -73,7 +73,7 @@
List<FakeEmailSender.Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
FakeEmailSender.Message msg = messages.get(0);
- assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(msg.rcpt()).containsExactly(user.getNameEmail());
assertThat(msg.body()).contains(admin.fullName() + " has removed a vote from this change.");
assertThat(msg.body())
.contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index ee5f117..069387c 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -830,7 +830,8 @@
private void addNoteDbCommit(Change.Id id, String commitMessage) throws Exception {
PersonIdent committer = serverIdent.get();
- PersonIdent author = noteUtil.newIdent(getAccount(admin.id()), committer.getWhen(), committer);
+ PersonIdent author =
+ noteUtil.newIdent(getAccount(admin.id()).id(), committer.getWhen(), committer);
serverSideTestRepo
.branch(RefNames.changeMetaRef(id))
.commit()
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
index 3b0e27b..9e0de92 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
@@ -38,8 +38,8 @@
MailMessage.Builder messageBuilderWithDefaultFields() {
MailMessage.Builder b = MailMessage.builder();
b.id("some id");
- b.from(user.getEmailAddress());
- b.addTo(user.getEmailAddress()); // Not evaluated
+ b.from(user.getNameEmail());
+ b.addTo(user.getNameEmail()); // Not evaluated
b.subject("");
b.dateReceived(Instant.now());
return b;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index 502194a..49f25e4 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -237,7 +237,7 @@
newPlaintextBody(getChangeUrl(changeInfo) + "/1", "Test Message", null, null, null);
MailMessage.Builder b =
messageBuilderWithDefaultFields()
- .from(user.getEmailAddress())
+ .from(user.getNameEmail())
.textContent(txt + textFooterForChange(changeInfo._number, ts));
sender.clear();
@@ -259,7 +259,7 @@
newPlaintextBody(getChangeUrl(changeInfo) + "/1", "Test Message", null, null, null);
MailMessage.Builder b =
messageBuilderWithDefaultFields()
- .from(user.getEmailAddress())
+ .from(user.getNameEmail())
.textContent(txt + textFooterForChange(changeInfo._number, ts));
sender.clear();
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index 29574c4..8b12b9d 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -241,7 +241,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@@ -273,7 +273,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -295,7 +295,7 @@
messages = sender.getMessages();
assertThat(messages).hasSize(1);
m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user2.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user2.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER_USER2\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@@ -322,7 +322,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Change subject: Document multiprimary setup\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -357,7 +357,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@@ -385,7 +385,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -407,7 +407,7 @@
messages = sender.getMessages();
assertThat(messages).hasSize(1);
m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user2.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user2.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER_USER2\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@@ -434,7 +434,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(user.getNameEmail());
assertThat(m.body()).contains("Change subject: Document multiprimary setup\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -547,7 +547,7 @@
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.getEmailAddress());
+ assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.getNameEmail());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
index 013939a..8ffcc8b 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
@@ -518,7 +518,9 @@
private RevCommit writeCommit(String body) throws Exception {
ChangeNoteUtil noteUtil = injector.getInstance(ChangeNoteUtil.class);
return writeCommit(
- body, noteUtil.newIdent(changeOwner.getAccount(), TimeUtil.nowTs(), serverIdent), false);
+ body,
+ noteUtil.newIdent(changeOwner.getAccount().id(), TimeUtil.nowTs(), serverIdent),
+ false);
}
private RevCommit writeCommit(String body, PersonIdent author) throws Exception {
@@ -529,7 +531,7 @@
ChangeNoteUtil noteUtil = injector.getInstance(ChangeNoteUtil.class);
return writeCommit(
body,
- noteUtil.newIdent(changeOwner.getAccount(), TimeUtil.nowTs(), serverIdent),
+ noteUtil.newIdent(changeOwner.getAccount().id(), TimeUtil.nowTs(), serverIdent),
initWorkInProgress);
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index 16981dc..68c7883 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -28,6 +28,7 @@
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
@@ -44,6 +45,7 @@
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AssigneeStatusUpdateProto;
+import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AttentionStatusProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
@@ -55,6 +57,7 @@
import com.google.protobuf.ByteString;
import java.lang.reflect.Type;
import java.sql.Timestamp;
+import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -64,6 +67,7 @@
import org.junit.Test;
public class ChangeNotesStateTest {
+
private static final Change.Id ID = Change.id(123);
private static final ObjectId SHA =
ObjectId.fromString("1234567812345678123456781234567812345678");
@@ -95,6 +99,13 @@
return ChangeNotesState.Builder.empty(ID).metaId(SHA).columns(cols);
}
+ private ChangeNotesStateProto.Builder newProtoBuilder() {
+ return ChangeNotesStateProto.newBuilder()
+ .setMetaId(SHA_BYTES)
+ .setChangeId(ID.get())
+ .setColumns(colsProto);
+ }
+
@Test
public void serializeChangeKey() throws Exception {
assertRoundTrip(
@@ -589,6 +600,39 @@
}
@Test
+ public void serializeAttentionUpdates() throws Exception {
+ assertRoundTrip(
+ newBuilder()
+ .attentionUpdates(
+ ImmutableList.of(
+ AttentionStatus.createFromRead(
+ Instant.EPOCH.plusSeconds(23),
+ Account.id(1000),
+ AttentionStatus.Operation.ADD,
+ "reason 1"),
+ AttentionStatus.createFromRead(
+ Instant.EPOCH.plusSeconds(42),
+ Account.id(2000),
+ AttentionStatus.Operation.REMOVE,
+ "reason 2")))
+ .build(),
+ newProtoBuilder()
+ .addAttentionStatus(
+ AttentionStatusProto.newBuilder()
+ .setDate(23_000) // epoch millis
+ .setAccount(1000)
+ .setOperation("ADD")
+ .setReason("reason 1"))
+ .addAttentionStatus(
+ AttentionStatusProto.newBuilder()
+ .setDate(42_000) // epoch millis
+ .setAccount(2000)
+ .setOperation("REMOVE")
+ .setReason("reason 2"))
+ .build());
+ }
+
+ @Test
public void serializeAssigneeUpdates() throws Exception {
assertRoundTrip(
newBuilder()
@@ -745,6 +789,9 @@
"reviewerUpdates",
new TypeLiteral<ImmutableList<ReviewerStatusUpdate>>() {}.getType())
.put(
+ "attentionUpdates",
+ new TypeLiteral<ImmutableList<AttentionStatus>>() {}.getType())
+ .put(
"assigneeUpdates",
new TypeLiteral<ImmutableList<AssigneeStatusUpdate>>() {}.getType())
.put("submitRecords", new TypeLiteral<ImmutableList<SubmitRecord>>() {}.getType())
@@ -824,10 +871,10 @@
.hasFields(
ImmutableMap.of(
"table",
- new TypeLiteral<
- ImmutableTable<
- ReviewerStateInternal, Account.Id, Timestamp>>() {}.getType(),
- "accounts", new TypeLiteral<ImmutableSet<Account.Id>>() {}.getType()));
+ new TypeLiteral<
+ ImmutableTable<ReviewerStateInternal, Account.Id, Timestamp>>() {}.getType(),
+ "accounts",
+ new TypeLiteral<ImmutableSet<Account.Id>>() {}.getType()));
}
@Test
@@ -836,9 +883,10 @@
.hasFields(
ImmutableMap.of(
"table",
- new TypeLiteral<
- ImmutableTable<ReviewerStateInternal, Address, Timestamp>>() {}.getType(),
- "users", new TypeLiteral<ImmutableSet<Address>>() {}.getType()));
+ new TypeLiteral<
+ ImmutableTable<ReviewerStateInternal, Address, Timestamp>>() {}.getType(),
+ "users",
+ new TypeLiteral<ImmutableSet<Address>>() {}.getType()));
}
@Test
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index db0dec8..4e068ba 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -38,6 +38,8 @@
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AttentionStatus;
+import com.google.gerrit.entities.AttentionStatus.Operation;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
@@ -58,6 +60,7 @@
import com.google.gerrit.testing.TestChanges;
import com.google.inject.Inject;
import java.sql.Timestamp;
+import java.time.Instant;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -688,6 +691,92 @@
}
@Test
+ public void defaultAttentionSetIsEmpty() throws Exception {
+ Change c = newChange();
+ ChangeNotes notes = newNotes(c);
+ assertThat(notes.getAttentionUpdates()).isEmpty();
+ }
+
+ @Test
+ public void addAttentionStatus() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ AttentionStatus attentionStatus =
+ AttentionStatus.createForWrite(changeOwner.getAccountId(), Operation.ADD, "test");
+ update.setAttentionUpdates(ImmutableList.of(attentionStatus));
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ assertThat(notes.getAttentionUpdates()).containsExactly(addTimestamp(attentionStatus, c));
+ }
+
+ @Test
+ public void filterLatestAttentionStatus() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ AttentionStatus attentionStatus =
+ AttentionStatus.createForWrite(changeOwner.getAccountId(), Operation.ADD, "test");
+ update.setAttentionUpdates(ImmutableList.of(attentionStatus));
+ update.commit();
+ update = newUpdate(c, changeOwner);
+ attentionStatus =
+ AttentionStatus.createForWrite(changeOwner.getAccountId(), Operation.REMOVE, "test");
+ update.setAttentionUpdates(ImmutableList.of(attentionStatus));
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ assertThat(notes.getAttentionUpdates()).containsExactly(addTimestamp(attentionStatus, c));
+ }
+
+ @Test
+ public void addAttentionStatus_rejectTimestamp() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ AttentionStatus attentionStatus =
+ AttentionStatus.createFromRead(
+ Instant.now(), changeOwner.getAccountId(), Operation.ADD, "test");
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> update.setAttentionUpdates(ImmutableList.of(attentionStatus)));
+ assertThat(thrown).hasMessageThat().contains("must not specify timestamp for write");
+ }
+
+ @Test
+ public void addAttentionStatus_rejectMultiplePerUser() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ AttentionStatus attentionStatus0 =
+ AttentionStatus.createForWrite(changeOwner.getAccountId(), Operation.ADD, "test 0");
+ AttentionStatus attentionStatus1 =
+ AttentionStatus.createForWrite(changeOwner.getAccountId(), Operation.ADD, "test 1");
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> update.setAttentionUpdates(ImmutableList.of(attentionStatus0, attentionStatus1)));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("must not specify multiple updates for single user");
+ }
+
+ @Test
+ public void addAttentionStatusForMultipleUsers() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ AttentionStatus attentionStatus0 =
+ AttentionStatus.createForWrite(changeOwner.getAccountId(), Operation.ADD, "test");
+ AttentionStatus attentionStatus1 =
+ AttentionStatus.createForWrite(otherUser.getAccountId(), Operation.ADD, "test");
+
+ update.setAttentionUpdates(ImmutableList.of(attentionStatus0, attentionStatus1));
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ assertThat(notes.getAttentionUpdates())
+ .containsExactly(addTimestamp(attentionStatus0, c), addTimestamp(attentionStatus1, c));
+ }
+
+ @Test
public void assigneeCommit() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
@@ -3140,4 +3229,13 @@
update.commit();
return tr.parseBody(commit);
}
+
+ private AttentionStatus addTimestamp(AttentionStatus attentionStatus, Change c) {
+ Timestamp timestamp = newNotes(c).getChange().getLastUpdatedOn();
+ return AttentionStatus.createFromRead(
+ timestamp.toInstant(),
+ attentionStatus.account(),
+ attentionStatus.operation(),
+ attentionStatus.reason());
+ }
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index bfe5ddc..bd21015 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -535,25 +535,24 @@
</div>
</section>
- <template is="dom-if" if="[[_showPrimaryTabs]]">
- <paper-tabs id="primaryTabs" on-selected-changed="_handleFileTabChange">
- <paper-tab>Files</paper-tab>
- <template is="dom-repeat" items="[[_dynamicTabHeaderEndpoints]]"
- as="tabHeader">
- <paper-tab>
- <gr-endpoint-decorator name$="[[tabHeader]]">
- <gr-endpoint-param name="change" value="[[_change]]">
- </gr-endpoint-param>
- <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
- </gr-endpoint-param>
- </gr-endpoint-decorator>
- </paper-tab>
- </template>
- </paper-tabs>
- </template>
-
<section class="patchInfo">
- <div hidden$="[[!_showFileTabContent]]">
+ <template is="dom-if" if="[[_showPrimaryTabs]]">
+ <paper-tabs id="primaryTabs" on-selected-changed="_handleFileTabChange">
+ <paper-tab data-name$="[[_files_tab_name]]">Files</paper-tab>
+ <template is="dom-repeat" items="[[_dynamicTabHeaderEndpoints]]"
+ as="tabHeader">
+ <paper-tab data-name$="[[tabHeader]]">
+ <gr-endpoint-decorator name$="[[tabHeader]]">
+ <gr-endpoint-param name="change" value="[[_change]]">
+ </gr-endpoint-param>
+ <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </paper-tab>
+ </template>
+ </paper-tabs>
+ </template>
+ <div hidden$="[[!_findIfTabMatches(_currentTabName, _files_tab_name)]]">
<gr-file-list-header
id="fileListHeader"
account="[[_account]]"
@@ -604,13 +603,13 @@
on-reload-drafts="_reloadDraftsWithCallback"></gr-file-list>
</div>
- <template is="dom-if" if="[[!_showFileTabContent]]">
- <gr-endpoint-decorator name$="[[_selectedFilesTabPluginEndpoint]]">
- <gr-endpoint-param name="change" value="[[_change]]">
- </gr-endpoint-param>
- <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
- </gr-endpoint-param>
- </gr-endpoint-decorator>
+ <template is="dom-if" if="[[_findIfTabMatches(_currentTabName, _selectedTabPluginHeader)]]">
+ <gr-endpoint-decorator name$="[[_selectedTabPluginEndpoint]]">
+ <gr-endpoint-param name="change" value="[[_change]]">
+ </gr-endpoint-param>
+ <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
</template>
</section>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index a90fe46..b55d964 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -68,6 +68,7 @@
const CHANGE_DATA_TIMING_LABEL = 'ChangeDataLoaded';
const CHANGE_RELOAD_TIMING_LABEL = 'ChangeReloaded';
const SEND_REPLY_TIMING_LABEL = 'SendReply';
+ const FILES_TAB_NAME = 'files';
/**
* @appliesMixin Gerrit.FireMixin
@@ -322,7 +323,12 @@
_dynamicTabContentEndpoints: {
type: Array,
},
- _selectedFilesTabPluginEndpoint: {
+ // The dynamic content of the plugin added tab
+ _selectedTabPluginEndpoint: {
+ type: String,
+ },
+ // The dynamic heading of the plugin added tab
+ _selectedTabPluginHeader: {
type: String,
},
_robotCommentsPatchSetDropdownItems: {
@@ -333,6 +339,14 @@
_currentRobotCommentsPatchSet: {
type: Number,
},
+ _files_tab_name: {
+ type: String,
+ value: FILES_TAB_NAME,
+ },
+ _currentTabName: {
+ type: String,
+ value: FILES_TAB_NAME,
+ },
};
}
@@ -503,30 +517,44 @@
return currentView === view;
}
- _handleFileTabChange(e) {
- const selectedIndex = this.shadowRoot
- .querySelector('#primaryTabs').selected;
- this._showFileTabContent = selectedIndex === 0;
- // Initial tab is the static files list.
- const newSelectedTab =
- this._dynamicTabContentEndpoints[selectedIndex - 1];
- if (newSelectedTab !== this._selectedFilesTabPluginEndpoint) {
- this._selectedFilesTabPluginEndpoint = newSelectedTab;
+ _findIfTabMatches(currentTab, tab) {
+ return currentTab === tab;
+ }
- const tabName = this._selectedFilesTabPluginEndpoint || 'files';
- const source = e && e.type ? e.type : '';
- this.$.reporting.reportInteraction('tab-changed', {tabName, source});
+ _handleFileTabChange(e) {
+ const selectedIndex = e.target.selected;
+ const tabs = e.target.querySelectorAll('paper-tab');
+ this._currentTabName = tabs[selectedIndex] &&
+ tabs[selectedIndex].dataset.name;
+ const source = e && e.type ? e.type : '';
+ const pluginIndex = this._dynamicTabHeaderEndpoints.indexOf(
+ this._currentTabName);
+ if (pluginIndex !== -1) {
+ this._selectedTabPluginEndpoint = this._dynamicTabContentEndpoints[
+ pluginIndex];
+ this._selectedTabPluginHeader = this._dynamicTabHeaderEndpoints[
+ pluginIndex];
+ } else {
+ this._selectedTabPluginEndpoint = '';
+ this._selectedTabPluginHeader = '';
}
+ this.$.reporting.reportInteraction('tab-changed',
+ {tabName: this._currentTabName, source});
}
_handleShowTab(e) {
- const idx = this._dynamicTabContentEndpoints.indexOf(e.detail.tab);
+ const primaryTabs = this.shadowRoot.querySelector('#primaryTabs');
+ const tabs = primaryTabs.querySelectorAll('paper-tab');
+ let idx = -1;
+ tabs.forEach((tab, index) => {
+ if (tab.dataset.name === e.detail.tab) idx = index;
+ });
if (idx === -1) {
- console.warn(e.detail.tab + ' tab not found');
+ console.error(e.detail.tab + ' tab not found');
return;
}
- this.shadowRoot.querySelector('#primaryTabs').selected = idx + 1;
- this.shadowRoot.querySelector('#primaryTabs').scrollIntoView();
+ primaryTabs.selected = idx;
+ primaryTabs.scrollIntoView();
this.$.reporting.reportInteraction('show-tab', {tabName: e.detail.tab});
}
@@ -1442,8 +1470,14 @@
} else {
this._selectedRevision =
Object.values(this._change.revisions).find(
- revision => revision._number ===
- parseInt(this._patchRange.patchNum, 10));
+ revision => {
+ // edit patchset is a special one
+ const thePatchNum = this._patchRange.patchNum;
+ if (thePatchNum === 'edit') {
+ return revision._number === thePatchNum;
+ }
+ return revision._number === parseInt(thePatchNum, 10);
+ });
}
});
}
@@ -1935,8 +1969,12 @@
if (!this._selectedRevision) {
return;
}
- // If patchNumStr is"edit", then patchNum is undefined hence an OR
- const patchNum = parseInt(patchNumStr, 10) || patchNumStr;
+
+ let patchNum = parseInt(patchNumStr, 10);
+ if (patchNumStr === 'edit') {
+ patchNum = patchNumStr;
+ }
+
if (patchNum === this._selectedRevision._number) {
return;
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index 934eeaf..3fe8faf 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -280,6 +280,20 @@
element = fixture('basic');
sandbox.stub(element.$.actions, 'reload').returns(Promise.resolve());
Gerrit._loadPlugins([]);
+ Gerrit.install(
+ plugin => {
+ plugin.registerDynamicCustomComponent(
+ 'change-view-tab-header',
+ 'gr-checks-change-view-tab-header-view'
+ );
+ plugin.registerDynamicCustomComponent(
+ 'change-view-tab-content',
+ 'gr-checks-view'
+ );
+ },
+ '0.1',
+ 'http://some/plugins/url.html'
+ );
});
teardown(done => {
@@ -305,6 +319,44 @@
assert.isTrue(replaceStateStub.called);
});
+ suite('plugins adding to file tab', () => {
+ setup(done => {
+ // Resolving it here instead of during setup() as other tests depend
+ // on flush() not being called during setup.
+ flush(() => done());
+ });
+
+ test('plugin added tab shows up as a dynamic endpoint', () => {
+ assert(element._dynamicTabHeaderEndpoints.includes(
+ 'change-view-tab-header-url'));
+ const paperTabs = element.shadowRoot.querySelector('#primaryTabs');
+ assert.equal(paperTabs.querySelectorAll('paper-tab').length, 2);
+ assert.equal(paperTabs.querySelectorAll('paper-tab')[1].dataset.name,
+ 'change-view-tab-header-url');
+ });
+
+ test('handleShowTab switched tab correctly', done => {
+ const paperTabs = element.shadowRoot.querySelector('#primaryTabs');
+ assert.equal(paperTabs.selected, 0);
+ element._handleShowTab({detail:
+ {tab: 'change-view-tab-header-url'}});
+ flush(() => {
+ assert.equal(paperTabs.selected, 1);
+ done();
+ });
+ });
+
+ test('switching tab sets _selectedTabPluginEndpoint', done => {
+ const paperTabs = element.shadowRoot.querySelector('#primaryTabs');
+ MockInteractions.tap(paperTabs.querySelectorAll('paper-tab')[1]);
+ flush(() => {
+ assert.equal(element._selectedTabPluginEndpoint,
+ 'change-view-tab-content-url');
+ done();
+ });
+ });
+ });
+
suite('keyboard shortcuts', () => {
test('t to add topic', () => {
const editStub = sandbox.stub(element.$.metadata, 'editTopic');
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.html b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.html
index 06edb9b..c650bb5 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.html
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.html
@@ -76,10 +76,10 @@
</div>
<div slot="footer" class="fix-picker" hidden$="[[hasSingleFix(_fixSuggestions)]]">
<span>Suggested fix [[addOneTo(_selectedFixIdx)]] of [[_fixSuggestions.length]]</span>
- <gr-button on-click="_onPrevFixClick" disabled$="[[_noPrevFix(_selectedFixIdx)]]">
+ <gr-button id="prevFix" on-click="_onPrevFixClick" disabled$="[[_noPrevFix(_selectedFixIdx)]]">
<iron-icon icon="gr-icons:chevron-left"></iron-icon>
</gr-button>
- <gr-button on-click="_onNextFixClick" disabled$="[[_noNextFix(_selectedFixIdx)]]">
+ <gr-button id="nextFix" on-click="_onNextFixClick" disabled$="[[_noNextFix(_selectedFixIdx, _fixSuggestions)]]">
<iron-icon icon="gr-icons:chevron-right"></iron-icon>
</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
index 033ef02..94549ee 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
@@ -140,8 +140,8 @@
_onNextFixClick(e) {
if (e) e.stopPropagation();
- if (this._selectedFixIdx < this._fixSuggestions.length &&
- this._fixSuggestions != null) {
+ if (this._fixSuggestions &&
+ this._selectedFixIdx < this._fixSuggestions.length) {
this._selectedFixIdx += 1;
return this._showSelectedFixSuggestion(
this._fixSuggestions[this._selectedFixIdx]);
@@ -152,9 +152,9 @@
return _selectedFixIdx === 0;
},
- _noNextFix(_selectedFixIdx) {
- if (this._fixSuggestions == null) return true;
- return _selectedFixIdx === this._fixSuggestions.length - 1;
+ _noNextFix(_selectedFixIdx, fixSuggestions) {
+ if (fixSuggestions == null) return true;
+ return _selectedFixIdx === fixSuggestions.length - 1;
},
_close() {
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html
index f9bbd88..f3f748f 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html
@@ -17,7 +17,7 @@
-->
<meta name='viewport' content='width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes'>
<title>gr-apply-fix-dialog</title>
-<script src='/test/common-test-setup.js'></script>
+<link rel='import' href='../../../test/common-test-setup.html'>
<script src='/bower_components/webcomponentsjs/custom-elements-es5-adapter.js'></script>
<script src='/bower_components/webcomponentsjs/webcomponents-lite.js'></script>
@@ -40,11 +40,16 @@
await readyToTest();
let element;
let sandbox;
- const ROBOT_COMMENT = {
+ const ROBOT_COMMENT_WITH_TWO_FIXES = {
robot_id: 'robot_1',
fix_suggestions: [{fix_id: 'fix_1'}, {fix_id: 'fix_2'}],
};
+ const ROBOT_COMMENT_WITH_ONE_FIX = {
+ robot_id: 'robot_1',
+ fix_suggestions: [{fix_id: 'fix_1'}],
+ };
+
setup(() => {
sandbox = sinon.sandbox.create();
element = fixture('basic');
@@ -101,7 +106,7 @@
}));
sandbox.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
- element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT}})
+ element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_TWO_FIXES}})
.then(() => {
assert.equal(element._currentFix.fix_id, 'fix_1');
assert.equal(element._currentPreviews.length, 2);
@@ -110,6 +115,22 @@
});
});
+ test('next button state updated when suggestions changed', done => {
+ sandbox.stub(element.$.restAPI, 'getRobotCommentFixPreview')
+ .returns(Promise.resolve({}));
+ sandbox.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
+
+ element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_ONE_FIX}})
+ .then(() => assert.isTrue(element.$.nextFix.disabled))
+ .then(() =>
+ element.open({detail: {patchNum: 2,
+ comment: ROBOT_COMMENT_WITH_TWO_FIXES}}))
+ .then(() => {
+ assert.isFalse(element.$.nextFix.disabled);
+ done();
+ });
+ });
+
test('preview endpoint throws error should reset dialog', done => {
sandbox.stub(window, 'fetch', (url => {
if (url.endsWith('/preview')) {
@@ -123,7 +144,8 @@
}));
const errorStub = sinon.stub();
document.addEventListener('network-error', errorStub);
- element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT}});
+ element.open({detail: {patchNum: 2,
+ comment: ROBOT_COMMENT_WITH_TWO_FIXES}});
flush(() => {
assert.isTrue(errorStub.called);
assert.deepEqual(element._currentFix, {});
@@ -189,7 +211,7 @@
}));
sandbox.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
- element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT}})
+ element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_TWO_FIXES}})
.then(() => {
element._onNextFixClick();
assert.equal(element._currentFix.fix_id, 'fix_2');
diff --git a/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html b/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html
index 6e62b15..305702a 100644
--- a/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html
@@ -19,7 +19,7 @@
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-api-interface</title>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<script src="../../../test/test-pre-setup.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
index 5fad3f4..f64d9ef 100644
--- a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
@@ -23,8 +23,8 @@
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="/bower_components/web-component-tester/browser.js"></script>
-<script src="../../../test/test-pre-setup.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../test/test-pre-setup.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
<script src="gr-email-suggestions-provider.js"></script>
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
index 7d89c2a..c46b443 100644
--- a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
@@ -23,8 +23,8 @@
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="/bower_components/web-component-tester/browser.js"></script>
-<script src="../../../test/test-pre-setup.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../test/test-pre-setup.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
<script src="gr-group-suggestions-provider.js"></script>
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
index af7b4bd..9696c2e 100644
--- a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
@@ -23,8 +23,8 @@
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="/bower_components/web-component-tester/browser.js"></script>
-<script src="../../../test/test-pre-setup.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../test/test-pre-setup.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
<script src="gr-reviewer-suggestions-provider.js"></script>
diff --git a/proto/cache.proto b/proto/cache.proto
index 8f73388..b881e3d 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -75,7 +75,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: 23
+// Next ID: 24
message ChangeNotesStateProto {
// Effectively required, even though the corresponding ChangeNotesState field
// is optional, since the field is only absent when NoteDb is disabled, in
@@ -133,7 +133,7 @@
// which case attempting to use the ChangeNotesCache is programmer error.
ChangeColumnsProto columns = 3;
- reserved 4; // past_assignee
+ reserved 4; // past_assignee
repeated string hashtag = 5;
@@ -167,6 +167,7 @@
// Next ID: 5
message ReviewerStatusUpdateProto {
+ // Epoch millis.
int64 date = 1;
int32 updated_by = 2;
int32 reviewer = 3;
@@ -194,14 +195,26 @@
bool has_server_id = 21;
message AssigneeStatusUpdateProto {
+ // Epoch millis.
int64 date = 1;
int32 updated_by = 2;
int32 current_assignee = 3;
bool has_current_assignee = 4;
}
repeated AssigneeStatusUpdateProto assignee_update = 22;
-}
+ // An update to the attention set of the change. See class AttentionStatus for
+ // context.
+ message AttentionStatusProto {
+ // Epoch millis.
+ int64 date = 1;
+ int32 account = 2;
+ // Maps to enum AttentionStatus.Operation
+ string operation = 3;
+ string reason = 4;
+ }
+ repeated AttentionStatusProto attention_status = 23;
+}
// Serialized form of com.google.gerrit.server.query.change.ConflictKey
message ConflictKeyProto {