Accept in_reply_to to thread inline comments

The in_reply_to field supplies the id of another comment, enabling
threading. Because id is URL encoded to match a REST API we also
encode the in_reply_to field, allowing clients to correctly line up
messages by simple string equality.

Change-Id: Idc6af51873b9b8a12707c6f4df9d25b627e8b409
This commit is contained in:
Shawn O. Pearce
2012-11-23 19:46:40 -08:00
parent abaa4d9578
commit 04a17cfa75
6 changed files with 40 additions and 17 deletions

View File

@@ -185,4 +185,8 @@ public final class PatchLineComment {
public String getParentUuid() {
return parentUuid;
}
public void setParentUuid(String inReplyTo) {
parentUuid = inReplyTo;
}
}

View File

@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.PutDraft.Input;
import com.google.gerrit.server.util.Url;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -60,7 +61,7 @@ class CreateDraft implements RestModifyView<RevisionResource, Input> {
ChangeUtil.messageUUID(db.get())),
in.line != null ? in.line : 0,
rsrc.getAuthorId(),
null);
Url.decode(in.inReplyTo));
c.setStatus(Status.DRAFT);
c.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
c.setMessage(in.message.trim());

View File

@@ -41,6 +41,7 @@ class GetDraft implements RestReadView<DraftResource> {
String path;
Side side;
Integer line;
String inReplyTo;
String message;
Timestamp updated;
@@ -53,6 +54,7 @@ class GetDraft implements RestReadView<DraftResource> {
if (c.getLine() > 0) {
line = c.getLine();
}
inReplyTo = Url.encode(c.getParentUuid());
message = Strings.emptyToNull(c.getMessage());
updated = c.getWrittenOn();
}

View File

@@ -39,6 +39,7 @@ import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.PostReview.Input;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.util.Url;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -85,6 +86,7 @@ public class PostReview implements RestModifyView<RevisionResource, Input> {
String id;
GetDraft.Side side;
int line;
String inReplyTo;
String message;
}
@@ -261,6 +263,7 @@ public class PostReview implements RestModifyView<RevisionResource, Input> {
for (Map.Entry<String, List<Comment>> ent : in.entrySet()) {
String path = ent.getKey();
for (Comment c : ent.getValue()) {
String parent = Url.decode(c.inReplyTo);
PatchLineComment e = drafts.remove(c.id);
boolean create = e == null;
if (create) {
@@ -270,7 +273,9 @@ public class PostReview implements RestModifyView<RevisionResource, Input> {
ChangeUtil.messageUUID(db)),
c.line,
rsrc.getAuthorId(),
null);
parent);
} else if (parent != null) {
e.setParentUuid(parent);
}
e.setStatus(PatchLineComment.Status.PUBLISHED);
e.setWrittenOn(timestamp);

View File

@@ -24,6 +24,8 @@ import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.GetDraft.Side;
import com.google.gerrit.server.change.PutDraft.Input;
import com.google.gerrit.server.util.Url;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -37,6 +39,7 @@ class PutDraft implements RestModifyView<DraftResource, Input> {
String path;
Side side;
Integer line;
String inReplyTo;
Timestamp updated; // Accepted but ignored.
@DefaultInput
@@ -58,9 +61,9 @@ class PutDraft implements RestModifyView<DraftResource, Input> {
}
@Override
public Object apply(DraftResource rsrc, Input in)
throws AuthException, BadRequestException, ResourceConflictException,
Exception {
public Object apply(DraftResource rsrc, Input in) throws AuthException,
BadRequestException, ResourceConflictException, OrmException {
PatchLineComment c = rsrc.getComment();
if (in == null || in.message == null || in.message.trim().isEmpty()) {
return delete.get().apply(rsrc, null);
} else if (in.kind != null && !"gerritcodereview#comment".equals(in.kind)) {
@@ -69,20 +72,19 @@ class PutDraft implements RestModifyView<DraftResource, Input> {
throw new BadRequestException("line must be >= 0");
}
PatchLineComment c = rsrc.getComment();
if (in.path != null
&& !in.path.equals(c.getKey().getParentKey().getFileName())) {
// Updating the path alters the primary key, which isn't possible.
// Delete then recreate the comment instead of an update.
db.get().patchComments().delete(Collections.singleton(c));
c = update(new PatchLineComment(
c = new PatchLineComment(
new PatchLineComment.Key(
new Patch.Key(rsrc.getPatchSet().getId(), in.path),
c.getKey().get()),
c.getLine(),
rsrc.getAuthorId(),
c.getParentUuid()), in);
db.get().patchComments().insert(Collections.singleton(c));
c.getParentUuid());
db.get().patchComments().insert(Collections.singleton(update(c, in)));
} else {
db.get().patchComments().update(Collections.singleton(update(c, in)));
}
@@ -96,6 +98,9 @@ class PutDraft implements RestModifyView<DraftResource, Input> {
if (in.line != null) {
e.setLine(in.line);
}
if (in.inReplyTo != null) {
e.setParentUuid(Url.decode(in.inReplyTo));
}
e.setMessage(in.message.trim());
e.updated();
return e;

View File

@@ -38,21 +38,27 @@ public final class Url {
* @return a string with all invalid URL characters escaped.
*/
public static String encode(String component) {
if (component != null) {
try {
return URLEncoder.encode(component, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("JVM must support UTF-8", e);
}
}
return null;
}
/** Decode a URL encoded string, e.g. from {@code "%2F"} to {@code "/"}. */
public static String decode(String str) {
if (str != null) {
try {
return URLDecoder.decode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("JVM must support UTF-8", e);
}
}
return null;
}
private Url() {
}