Add option to omit duplicate comments.

Change-Id: I6afc938ad72afbcfd5cb653b88f7e65431d2018c
This commit is contained in:
Bill Wendling 2015-10-19 15:40:57 -07:00 committed by Jonathan Nieder
parent d218e4078c
commit 692b4ec251
6 changed files with 114 additions and 16 deletions

View File

@ -4491,7 +4491,7 @@ label names to the voting values.
|`comments` |optional| |`comments` |optional|
The comments that should be added as a map that maps a file path to a The comments that should be added as a map that maps a file path to a
list of link:#comment-input[CommentInput] entities. list of link:#comment-input[CommentInput] entities.
|`strict_labels`|`true` if not set| |`strict_labels` |`true` if not set|
Whether all labels are required to be within the user's permitted ranges Whether all labels are required to be within the user's permitted ranges
based on access controls. + based on access controls. +
If `true`, attempting to use a label not granted to the user will fail If `true`, attempting to use a label not granted to the user will fail
@ -4511,7 +4511,9 @@ Notify handling that defines to whom email notifications should be sent
after the review is stored. + after the review is stored. +
Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. + Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
If not set, the default is `ALL`. If not set, the default is `ALL`.
|`on_behalf_of`|optional| |`omit_duplicate_comments`|optional|
If `true`, comments with the same content at the same place will be omitted.
|`on_behalf_of` |optional|
link:rest-api-accounts.html#account-id[\{account-id\}] the review link:rest-api-accounts.html#account-id[\{account-id\}] the review
should be posted on behalf of. To use this option the caller must should be posted on behalf of. To use this option the caller must
have been granted `labelAs-NAME` permission for all keys of labels. have been granted `labelAs-NAME` permission for all keys of labels.

View File

@ -188,12 +188,35 @@ public class CommentsIT extends AbstractDaemonTest {
} }
} }
@Test
public void addDuplicateComments() throws Exception {
PushOneCommit.Result r1 = createChange();
String changeId = r1.getChangeId();
String revId = r1.getCommit().getName();
addComment(r1, "nit: trailing whitespace");
addComment(r1, "nit: trailing whitespace");
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result.get(FILE_NAME)).hasSize(2);
addComment(r1, "nit: trailing whitespace", true);
result = getPublishedComments(changeId, revId);
assertThat(result.get(FILE_NAME)).hasSize(2);
PushOneCommit.Result r2 = pushFactory.create(
db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "content")
.to("refs/for/master");
changeId = r2.getChangeId();
revId = r2.getCommit().getName();
addComment(r2, "nit: trailing whitespace", true);
result = getPublishedComments(changeId, revId);
assertThat(result.get(FILE_NAME)).hasSize(1);
}
@Test @Test
public void listChangeDrafts() throws Exception { public void listChangeDrafts() throws Exception {
PushOneCommit.Result r1 = createChange(); PushOneCommit.Result r1 = createChange();
PushOneCommit.Result r2 = pushFactory.create( PushOneCommit.Result r2 = pushFactory.create(
db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content",
r1.getChangeId()) r1.getChangeId())
.to("refs/for/master"); .to("refs/for/master");
@ -363,9 +386,13 @@ public class CommentsIT extends AbstractDaemonTest {
+ "-- \n"); + "-- \n");
} }
private void addComment(PushOneCommit.Result r, String message) private void addComment(PushOneCommit.Result r, String message)
throws Exception { throws Exception {
addComment(r, message, false);
}
private void addComment(PushOneCommit.Result r, String message,
boolean omitDuplicateComments) throws Exception {
CommentInput c = new CommentInput(); CommentInput c = new CommentInput();
c.line = 1; c.line = 1;
c.message = message; c.message = message;
@ -373,6 +400,7 @@ public class CommentsIT extends AbstractDaemonTest {
ReviewInput in = new ReviewInput(); ReviewInput in = new ReviewInput();
in.comments = ImmutableMap.<String, List<CommentInput>> of( in.comments = ImmutableMap.<String, List<CommentInput>> of(
FILE_NAME, ImmutableList.of(c)); FILE_NAME, ImmutableList.of(c));
in.omitDuplicateComments = omitDuplicateComments;
gApi.changes() gApi.changes()
.id(r.getChangeId()) .id(r.getChangeId())
.revision(r.getCommit().name()) .revision(r.getCommit().name())

View File

@ -48,6 +48,12 @@ public class ReviewInput {
/** Who to send email notifications to after review is stored. */ /** Who to send email notifications to after review is stored. */
public NotifyHandling notify = NotifyHandling.ALL; public NotifyHandling notify = NotifyHandling.ALL;
/**
* If true check to make sure that the comments being posted aren't already
* present.
*/
public boolean omitDuplicateComments;
/** /**
* Account ID, name, email address or username of another user. The review * Account ID, name, email address or username of another user. The review
* will be posted/updated on behalf of this named user instead of the * will be posted/updated on behalf of this named user instead of the

View File

@ -15,5 +15,16 @@
package com.google.gerrit.extensions.client; package com.google.gerrit.extensions.client;
public enum Side { public enum Side {
PARENT, REVISION PARENT,
REVISION;
public static Side fromShort(short s) {
switch (s) {
case 0:
return PARENT;
case 1:
return REVISION;
}
return null;
}
} }

View File

@ -52,7 +52,7 @@ public final class PatchLineComment {
} }
@Override @Override
protected void set(String newValue) { public void set(String newValue) {
uuid = newValue; uuid = newValue;
} }

View File

@ -17,12 +17,17 @@ package com.google.gerrit.server.change;
import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId; import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.auto.value.AutoValue;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes; import com.google.gerrit.common.data.LabelTypes;
@ -75,9 +80,11 @@ import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
@Singleton @Singleton
@ -299,6 +306,32 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
} }
} }
/**
* Used to compare PatchLineComments with CommentInput comments.
*/
@AutoValue
abstract static class CommentSetEntry {
private static CommentSetEntry create(Patch.Key key,
Integer line, Side side, HashCode message, CommentRange range) {
return new AutoValue_PostReview_CommentSetEntry(key, line, side, message,
range);
}
public static CommentSetEntry create(PatchLineComment comment) {
return create(comment.getKey().getParentKey(),
comment.getLine(),
Side.fromShort(comment.getSide()),
Hashing.sha1().hashString(comment.getMessage(), UTF_8),
comment.getRange());
}
abstract Patch.Key key();
@Nullable abstract Integer line();
abstract Side side();
abstract HashCode message();
@Nullable abstract CommentRange range();
}
private class Op extends BatchUpdate.Op { private class Op extends BatchUpdate.Op {
private final PatchSet.Id psId; private final PatchSet.Id psId;
private final ReviewInput in; private final ReviewInput in;
@ -374,6 +407,10 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
List<PatchLineComment> del = Lists.newArrayList(); List<PatchLineComment> del = Lists.newArrayList();
List<PatchLineComment> ups = Lists.newArrayList(); List<PatchLineComment> ups = Lists.newArrayList();
Set<CommentSetEntry> existingIds = in.omitDuplicateComments
? readExistingComments(ctx)
: Collections.<CommentSetEntry>emptySet();
for (Map.Entry<String, List<CommentInput>> ent : map.entrySet()) { for (Map.Entry<String, List<CommentInput>> ent : map.entrySet()) {
String path = ent.getKey(); String path = ent.getKey();
for (CommentInput c : ent.getValue()) { for (CommentInput c : ent.getValue()) {
@ -381,9 +418,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
PatchLineComment e = drafts.remove(Url.decode(c.id)); PatchLineComment e = drafts.remove(Url.decode(c.id));
if (e == null) { if (e == null) {
e = new PatchLineComment( e = new PatchLineComment(
new PatchLineComment.Key( new PatchLineComment.Key(new Patch.Key(psId, path), null),
new Patch.Key(psId, path),
ChangeUtil.messageUUID(ctx.getDb())),
c.line != null ? c.line : 0, c.line != null ? c.line : 0,
user.getAccountId(), user.getAccountId(),
parent, ctx.getWhen()); parent, ctx.getWhen());
@ -403,6 +438,12 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
c.range.endCharacter)); c.range.endCharacter));
e.setLine(c.range.endLine); e.setLine(c.range.endLine);
} }
if (existingIds.contains(CommentSetEntry.create(e))) {
continue;
}
if (e.getKey().get() == null) {
e.getKey().set(ChangeUtil.messageUUID(ctx.getDb()));
}
ups.add(e); ups.add(e);
} }
} }
@ -430,6 +471,16 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
return !del.isEmpty() || !ups.isEmpty(); return !del.isEmpty() || !ups.isEmpty();
} }
private Set<CommentSetEntry> readExistingComments(ChangeContext ctx)
throws OrmException {
Set<CommentSetEntry> r = new HashSet<>();
for (PatchLineComment c : plcUtil.publishedByChange(ctx.getDb(),
ctx.getChangeNotes())) {
r.add(CommentSetEntry.create(c));
}
return r;
}
private Map<String, PatchLineComment> changeDrafts(ChangeContext ctx) private Map<String, PatchLineComment> changeDrafts(ChangeContext ctx)
throws OrmException { throws OrmException {
Map<String, PatchLineComment> drafts = Maps.newHashMap(); Map<String, PatchLineComment> drafts = Maps.newHashMap();