Merge changes I86a8c524,Icd3b72b1,I178b0395
* changes: Expose the history of all reviewers ever on a change Move Change out of AbstractChangeNotes Store draft PatchLineComments in Git notes
This commit is contained in:
@@ -43,6 +43,8 @@ public class RefNames {
|
|||||||
*/
|
*/
|
||||||
public static final String REFS_CACHE_AUTOMERGE = "refs/cache-automerge/";
|
public static final String REFS_CACHE_AUTOMERGE = "refs/cache-automerge/";
|
||||||
|
|
||||||
|
public static final String REFS_DRAFT_PREFIX = "comments-";
|
||||||
|
|
||||||
public static String refsUsers(Account.Id accountId) {
|
public static String refsUsers(Account.Id accountId) {
|
||||||
StringBuilder r = new StringBuilder();
|
StringBuilder r = new StringBuilder();
|
||||||
r.append(REFS_USER);
|
r.append(REFS_USER);
|
||||||
@@ -57,6 +59,16 @@ public class RefNames {
|
|||||||
return r.toString();
|
return r.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String refsDraftComments(Account.Id accountId,
|
||||||
|
Change.Id changeId) {
|
||||||
|
StringBuilder r = new StringBuilder();
|
||||||
|
r.append(refsUsers(accountId));
|
||||||
|
r.append('/');
|
||||||
|
r.append(REFS_DRAFT_PREFIX);
|
||||||
|
r.append(changeId.get());
|
||||||
|
return r.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private RefNames() {
|
private RefNames() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -77,6 +77,15 @@ public class PatchLineCommentsUtil {
|
|||||||
return commentsOnPs;
|
return commentsOnPs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(yyonas): Delete drafts if they already existed.
|
||||||
|
public void addPublishedComments(ReviewDb db, ChangeUpdate update,
|
||||||
|
Iterable<PatchLineComment> comments) throws OrmException {
|
||||||
|
for (PatchLineComment c : comments) {
|
||||||
|
update.upsertComment(c);
|
||||||
|
}
|
||||||
|
db.patchComments().upsert(comments);
|
||||||
|
}
|
||||||
|
|
||||||
private static Collection<PatchLineComment> addCommentsInFile(
|
private static Collection<PatchLineComment> addCommentsInFile(
|
||||||
Collection<PatchLineComment> commentsOnFile,
|
Collection<PatchLineComment> commentsOnFile,
|
||||||
Collection<PatchLineComment> allComments,
|
Collection<PatchLineComment> allComments,
|
||||||
@@ -89,12 +98,4 @@ public class PatchLineCommentsUtil {
|
|||||||
}
|
}
|
||||||
return commentsOnFile;
|
return commentsOnFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPublishedComments(ReviewDb db, ChangeUpdate update,
|
|
||||||
Iterable<PatchLineComment> comments) throws OrmException {
|
|
||||||
for (PatchLineComment c : comments) {
|
|
||||||
update.putComment(c);
|
|
||||||
}
|
|
||||||
db.patchComments().upsert(comments);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -175,6 +175,7 @@ public abstract class VersionedMetaData {
|
|||||||
void write(CommitBuilder commit) throws IOException;
|
void write(CommitBuilder commit) throws IOException;
|
||||||
void write(VersionedMetaData config, CommitBuilder commit) throws IOException;
|
void write(VersionedMetaData config, CommitBuilder commit) throws IOException;
|
||||||
RevCommit createRef(String refName) throws IOException;
|
RevCommit createRef(String refName) throws IOException;
|
||||||
|
void removeRef(String refName) throws IOException;
|
||||||
RevCommit commit() throws IOException;
|
RevCommit commit() throws IOException;
|
||||||
RevCommit commitAt(ObjectId revision) throws IOException;
|
RevCommit commitAt(ObjectId revision) throws IOException;
|
||||||
void close();
|
void close();
|
||||||
@@ -267,6 +268,24 @@ public abstract class VersionedMetaData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRef(String refName) throws IOException {
|
||||||
|
RefUpdate ru = db.updateRef(refName);
|
||||||
|
ru.setForceUpdate(true);
|
||||||
|
if (revision != null) {
|
||||||
|
ru.setExpectedOldObjectId(revision);
|
||||||
|
}
|
||||||
|
RefUpdate.Result result = ru.delete();
|
||||||
|
switch (result) {
|
||||||
|
case FORCED:
|
||||||
|
update.fireGitRefUpdatedEvent(ru);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw new IOException("Cannot delete " + ru.getName() + " in "
|
||||||
|
+ db.getDirectory() + ": " + ru.getResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RevCommit commit() throws IOException {
|
public RevCommit commit() throws IOException {
|
||||||
return commitAt(revision);
|
return commitAt(revision);
|
||||||
|
@@ -29,19 +29,15 @@ import java.io.IOException;
|
|||||||
public abstract class AbstractChangeNotes<T> extends VersionedMetaData {
|
public abstract class AbstractChangeNotes<T> extends VersionedMetaData {
|
||||||
private boolean loaded;
|
private boolean loaded;
|
||||||
protected final GitRepositoryManager repoManager;
|
protected final GitRepositoryManager repoManager;
|
||||||
private final Change change;
|
private final Change.Id changeId;
|
||||||
|
|
||||||
AbstractChangeNotes(GitRepositoryManager repoManager, Change change) {
|
AbstractChangeNotes(GitRepositoryManager repoManager, Change.Id changeId) {
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.change = new Change(change);
|
this.changeId = changeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Change.Id getChangeId() {
|
public Change.Id getChangeId() {
|
||||||
return change.getId();
|
return changeId;
|
||||||
}
|
|
||||||
|
|
||||||
public Change getChange() {
|
|
||||||
return change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T load() throws OrmException {
|
public T load() throws OrmException {
|
||||||
|
@@ -59,6 +59,10 @@ public abstract class AbstractChangeUpdate extends VersionedMetaData {
|
|||||||
this.when = when;
|
this.when = when;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChangeNotes getChangeNotes() {
|
||||||
|
return ctl.getNotes();
|
||||||
|
}
|
||||||
|
|
||||||
public Change getChange() {
|
public Change getChange() {
|
||||||
return ctl.getChange();
|
return ctl.getChange();
|
||||||
}
|
}
|
||||||
@@ -119,6 +123,11 @@ public abstract class AbstractChangeUpdate extends VersionedMetaData {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRef(String refName) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RevCommit commit() {
|
public RevCommit commit() {
|
||||||
return null;
|
return null;
|
||||||
|
@@ -0,0 +1,286 @@
|
|||||||
|
// Copyright (C) 2014 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.notedb;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchLineComment;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.reviewdb.client.RevId;
|
||||||
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
import com.google.inject.assistedinject.AssistedInject;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.notes.NoteMap;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single delta to apply atomically to a change.
|
||||||
|
* <p>
|
||||||
|
* This delta contains only draft comments on a single patch set of a change by
|
||||||
|
* a single author. This delta will become a single commit in the All-Users
|
||||||
|
* repository.
|
||||||
|
* <p>
|
||||||
|
* This class is not thread safe.
|
||||||
|
*/
|
||||||
|
public class ChangeDraftUpdate extends AbstractChangeUpdate {
|
||||||
|
public interface Factory {
|
||||||
|
ChangeDraftUpdate create(ChangeControl ctl, Date when);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AllUsersName draftsProject;
|
||||||
|
private final Account.Id accountId;
|
||||||
|
private final CommentsInNotesUtil commentsUtil;
|
||||||
|
private final ChangeNotes changeNotes;
|
||||||
|
private final DraftCommentNotes draftNotes;
|
||||||
|
|
||||||
|
private List<PatchLineComment> upsertComments;
|
||||||
|
private List<PatchLineComment> deleteComments;
|
||||||
|
|
||||||
|
@AssistedInject
|
||||||
|
private ChangeDraftUpdate(
|
||||||
|
@GerritPersonIdent PersonIdent serverIdent,
|
||||||
|
GitRepositoryManager repoManager,
|
||||||
|
NotesMigration migration,
|
||||||
|
MetaDataUpdate.User updateFactory,
|
||||||
|
DraftCommentNotes.Factory draftNotesFactory,
|
||||||
|
AllUsersName allUsers,
|
||||||
|
CommentsInNotesUtil commentsUtil,
|
||||||
|
@Assisted ChangeControl ctl,
|
||||||
|
@Assisted Date when) throws OrmException {
|
||||||
|
super(migration, repoManager, updateFactory, ctl, serverIdent, when);
|
||||||
|
this.draftsProject = allUsers;
|
||||||
|
this.commentsUtil = commentsUtil;
|
||||||
|
checkState(ctl.getCurrentUser().isIdentifiedUser(),
|
||||||
|
"Current user must be identified");
|
||||||
|
IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
|
||||||
|
this.accountId = user.getAccountId();
|
||||||
|
this.changeNotes = getChangeNotes().load();
|
||||||
|
this.draftNotes = draftNotesFactory.create(ctl.getChange().getId(),
|
||||||
|
user.getAccountId()).load();
|
||||||
|
|
||||||
|
this.upsertComments = Lists.newArrayList();
|
||||||
|
this.deleteComments = Lists.newArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertComment(PatchLineComment c) throws OrmException {
|
||||||
|
verifyComment(c);
|
||||||
|
checkArgument(c.getStatus() == Status.DRAFT,
|
||||||
|
"Cannot insert a published comment into a ChangeDraftUpdate");
|
||||||
|
checkArgument(!changeNotes.containsComment(c),
|
||||||
|
"A comment already exists with the same key,"
|
||||||
|
+ " so the following comment cannot be inserted: %s", c);
|
||||||
|
upsertComments.add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upsertComment(PatchLineComment c) {
|
||||||
|
verifyComment(c);
|
||||||
|
checkArgument(c.getStatus() == Status.DRAFT,
|
||||||
|
"Cannot upsert a published comment into a ChangeDraftUpdate");
|
||||||
|
upsertComments.add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateComment(PatchLineComment c) throws OrmException {
|
||||||
|
verifyComment(c);
|
||||||
|
checkArgument(c.getStatus() == Status.DRAFT,
|
||||||
|
"Cannot update a published comment into a ChangeDraftUpdate");
|
||||||
|
checkArgument(draftNotes.containsComment(c),
|
||||||
|
"Cannot update this comment because it didn't exist previously");
|
||||||
|
upsertComments.add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteComment(PatchLineComment c) {
|
||||||
|
verifyComment(c);
|
||||||
|
checkArgument(draftNotes.containsComment(c), "Cannot delete this comment"
|
||||||
|
+ " because it didn't previously exist as a draft");
|
||||||
|
deleteComments.add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a PatchLineComment from the list of drafts only if it existed
|
||||||
|
* previously as a draft. If it wasn't a draft previously, this is a no-op.
|
||||||
|
*/
|
||||||
|
public void deleteCommentIfPresent(PatchLineComment c) {
|
||||||
|
if (draftNotes.containsComment(c)) {
|
||||||
|
verifyComment(c);
|
||||||
|
deleteComments.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyComment(PatchLineComment comment) {
|
||||||
|
checkState(psId != null,
|
||||||
|
"setPatchSetId must be called first");
|
||||||
|
checkArgument(getCommentPsId(comment).equals(psId),
|
||||||
|
"Comment on %s does not match configured patch set %s",
|
||||||
|
getCommentPsId(comment), psId);
|
||||||
|
checkArgument(comment.getRevId() != null);
|
||||||
|
checkArgument(comment.getAuthor().equals(accountId),
|
||||||
|
"The author for the following comment does not match the author of"
|
||||||
|
+ " this ChangeDraftUpdate (%s): %s", accountId, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the tree id for the updated tree */
|
||||||
|
private ObjectId storeCommentsInNotes(AtomicBoolean removedAllComments)
|
||||||
|
throws OrmException, IOException {
|
||||||
|
if (isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
NoteMap noteMap = draftNotes.getNoteMap();
|
||||||
|
if (noteMap == null) {
|
||||||
|
noteMap = NoteMap.newEmptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Table<PatchSet.Id, String, PatchLineComment> baseDrafts =
|
||||||
|
draftNotes.getDraftBaseComments();
|
||||||
|
Table<PatchSet.Id, String, PatchLineComment> psDrafts =
|
||||||
|
draftNotes.getDraftPsComments();
|
||||||
|
|
||||||
|
// There is no need to rewrite the note for one of the sides of the patch
|
||||||
|
// set if all of the modifications were made to the comments of one side,
|
||||||
|
// so we set these flags to potentially save that extra work.
|
||||||
|
boolean baseSideChanged = false;
|
||||||
|
boolean revisionSideChanged = false;
|
||||||
|
|
||||||
|
// We must define these RevIds so that if this update deletes all
|
||||||
|
// remaining comments on a given side, then we can remove that note.
|
||||||
|
// However, if this update doesn't delete any comments, it is okay for these
|
||||||
|
// to be null because they won't be used.
|
||||||
|
RevId baseRevId = null;
|
||||||
|
RevId psRevId = null;
|
||||||
|
|
||||||
|
for (PatchLineComment c : deleteComments) {
|
||||||
|
if (c.getSide() == (short) 0) {
|
||||||
|
baseSideChanged = true;
|
||||||
|
baseRevId = c.getRevId();
|
||||||
|
baseDrafts.remove(psId, c.getKey().get());
|
||||||
|
} else {
|
||||||
|
revisionSideChanged = true;
|
||||||
|
psRevId = c.getRevId();
|
||||||
|
psDrafts.remove(psId, c.getKey().get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PatchLineComment c : upsertComments) {
|
||||||
|
if (c.getSide() == (short) 0) {
|
||||||
|
baseSideChanged = true;
|
||||||
|
baseDrafts.put(psId, c.getKey().get(), c);
|
||||||
|
} else {
|
||||||
|
revisionSideChanged = true;
|
||||||
|
psDrafts.put(psId, c.getKey().get(), c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PatchLineComment> newBaseDrafts =
|
||||||
|
Lists.newArrayList(baseDrafts.row(psId).values());
|
||||||
|
List<PatchLineComment> newPsDrafts =
|
||||||
|
Lists.newArrayList(psDrafts.row(psId).values());
|
||||||
|
|
||||||
|
updateNoteMap(baseSideChanged, noteMap, newBaseDrafts,
|
||||||
|
baseRevId);
|
||||||
|
updateNoteMap(revisionSideChanged, noteMap, newPsDrafts,
|
||||||
|
psRevId);
|
||||||
|
|
||||||
|
removedAllComments.set(baseDrafts.isEmpty() && psDrafts.isEmpty());
|
||||||
|
|
||||||
|
return noteMap.writeTree(inserter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNoteMap(boolean changed, NoteMap noteMap,
|
||||||
|
List<PatchLineComment> comments, RevId commitId)
|
||||||
|
throws IOException, OrmException {
|
||||||
|
if (changed) {
|
||||||
|
if (comments.isEmpty()) {
|
||||||
|
commentsUtil.removeNote(noteMap, commitId);
|
||||||
|
} else {
|
||||||
|
commentsUtil.writeCommentsToNoteMap(noteMap, comments, inserter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RevCommit commit() throws IOException {
|
||||||
|
BatchMetaDataUpdate batch = openUpdate();
|
||||||
|
try {
|
||||||
|
CommitBuilder builder = new CommitBuilder();
|
||||||
|
if (migration.write()) {
|
||||||
|
AtomicBoolean removedAllComments = new AtomicBoolean();
|
||||||
|
ObjectId treeId = storeCommentsInNotes(removedAllComments);
|
||||||
|
if (treeId != null) {
|
||||||
|
if (removedAllComments.get()) {
|
||||||
|
batch.removeRef(getRefName());
|
||||||
|
} else {
|
||||||
|
builder.setTreeId(treeId);
|
||||||
|
batch.write(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return batch.commit();
|
||||||
|
} catch (OrmException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} finally {
|
||||||
|
batch.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Project.NameKey getProjectName() {
|
||||||
|
return draftsProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getRefName() {
|
||||||
|
return RefNames.refsDraftComments(accountId, getChange().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onSave(CommitBuilder commit) throws IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
|
if (isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
commit.setAuthor(newIdent(getUser().getAccount(), when));
|
||||||
|
commit.setCommitter(new PersonIdent(serverIdent, when));
|
||||||
|
commit.setMessage(String.format("Comment on patch set %d", psId.get()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmpty() {
|
||||||
|
return deleteComments.isEmpty()
|
||||||
|
&& upsertComments.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
@@ -19,6 +19,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
|
|||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMITTED_WITH;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMITTED_WITH;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST;
|
||||||
|
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Enums;
|
import com.google.common.base.Enums;
|
||||||
@@ -49,8 +50,11 @@ import com.google.gerrit.reviewdb.client.PatchSet.Id;
|
|||||||
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSetApproval.LabelId;
|
import com.google.gerrit.reviewdb.client.PatchSetApproval.LabelId;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.util.LabelVote;
|
import com.google.gerrit.server.util.LabelVote;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
@@ -134,15 +138,17 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
@Singleton
|
@Singleton
|
||||||
public static class Factory {
|
public static class Factory {
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
|
private final AllUsersNameProvider allUsersProvider;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Inject
|
@Inject
|
||||||
public Factory(GitRepositoryManager repoManager) {
|
public Factory(GitRepositoryManager repoManager, AllUsersNameProvider allUsersProvider) {
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
|
this.allUsersProvider = allUsersProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeNotes create(Change change) {
|
public ChangeNotes create(Change change) {
|
||||||
return new ChangeNotes(repoManager, change);
|
return new ChangeNotes(repoManager, allUsersProvider, change);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +160,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
private final Map<PatchSet.Id,
|
private final Map<PatchSet.Id,
|
||||||
Table<Account.Id, String, Optional<PatchSetApproval>>> approvals;
|
Table<Account.Id, String, Optional<PatchSetApproval>>> approvals;
|
||||||
private final Map<Account.Id, ReviewerState> reviewers;
|
private final Map<Account.Id, ReviewerState> reviewers;
|
||||||
|
private final List<Account.Id> allPastReviewers;
|
||||||
private final List<SubmitRecord> submitRecords;
|
private final List<SubmitRecord> submitRecords;
|
||||||
private final Multimap<PatchSet.Id, ChangeMessage> changeMessages;
|
private final Multimap<PatchSet.Id, ChangeMessage> changeMessages;
|
||||||
private final Multimap<Id, PatchLineComment> commentsForPs;
|
private final Multimap<Id, PatchLineComment> commentsForPs;
|
||||||
@@ -170,6 +177,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
this.repo = repoManager.openRepository(getProjectName(change));
|
this.repo = repoManager.openRepository(getProjectName(change));
|
||||||
approvals = Maps.newHashMap();
|
approvals = Maps.newHashMap();
|
||||||
reviewers = Maps.newLinkedHashMap();
|
reviewers = Maps.newLinkedHashMap();
|
||||||
|
allPastReviewers = Lists.newArrayList();
|
||||||
submitRecords = Lists.newArrayListWithExpectedSize(1);
|
submitRecords = Lists.newArrayListWithExpectedSize(1);
|
||||||
changeMessages = LinkedListMultimap.create();
|
changeMessages = LinkedListMultimap.create();
|
||||||
commentsForPs = ArrayListMultimap.create();
|
commentsForPs = ArrayListMultimap.create();
|
||||||
@@ -182,6 +190,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
parse(commit);
|
parse(commit);
|
||||||
}
|
}
|
||||||
parseComments();
|
parseComments();
|
||||||
|
allPastReviewers.addAll(reviewers.keySet());
|
||||||
pruneReviewers();
|
pruneReviewers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,17 +493,30 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Change change;
|
||||||
private ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals;
|
private ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals;
|
||||||
private ImmutableSetMultimap<ReviewerState, Account.Id> reviewers;
|
private ImmutableSetMultimap<ReviewerState, Account.Id> reviewers;
|
||||||
|
private ImmutableList<Account.Id> allPastReviewers;
|
||||||
private ImmutableList<SubmitRecord> submitRecords;
|
private ImmutableList<SubmitRecord> submitRecords;
|
||||||
private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages;
|
private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages;
|
||||||
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForBase;
|
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForBase;
|
||||||
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForPS;
|
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForPS;
|
||||||
NoteMap noteMap;
|
NoteMap noteMap;
|
||||||
|
|
||||||
|
private final AllUsersName allUsers;
|
||||||
|
private DraftCommentNotes draftCommentNotes;
|
||||||
|
|
||||||
|
@Inject
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public ChangeNotes(GitRepositoryManager repoManager, Change change) {
|
public ChangeNotes(GitRepositoryManager repoManager,
|
||||||
super(repoManager, change);
|
AllUsersNameProvider allUsersProvider, Change change) {
|
||||||
|
super(repoManager, change.getId());
|
||||||
|
this.allUsers = allUsersProvider.get();
|
||||||
|
this.change = new Change(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Change getChange() {
|
||||||
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableListMultimap<PatchSet.Id, PatchSetApproval> getApprovals() {
|
public ImmutableListMultimap<PatchSet.Id, PatchSetApproval> getApprovals() {
|
||||||
@@ -505,6 +527,13 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
return reviewers;
|
return reviewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of all users who have ever been a reviewer on this change.
|
||||||
|
*/
|
||||||
|
public ImmutableList<Account.Id> getAllPastReviewers() {
|
||||||
|
return allPastReviewers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return submit records stored during the most recent submit; only for
|
* @return submit records stored during the most recent submit; only for
|
||||||
* changes that were actually submitted.
|
* changes that were actually submitted.
|
||||||
@@ -530,6 +559,55 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
return commentsForPS;
|
return commentsForPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Table<PatchSet.Id, String, PatchLineComment> getDraftBaseComments(
|
||||||
|
Account.Id author) throws OrmException {
|
||||||
|
loadDraftComments(author);
|
||||||
|
return draftCommentNotes.getDraftBaseComments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Table<PatchSet.Id, String, PatchLineComment> getDraftPsComments(
|
||||||
|
Account.Id author) throws OrmException {
|
||||||
|
loadDraftComments(author);
|
||||||
|
return draftCommentNotes.getDraftPsComments();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If draft comments have already been loaded for this author, then they will
|
||||||
|
* not be reloaded. However, this method will load the comments if no draft
|
||||||
|
* comments have been loaded or if the caller would like the drafts for
|
||||||
|
* another author.
|
||||||
|
*/
|
||||||
|
private void loadDraftComments(Account.Id author)
|
||||||
|
throws OrmException {
|
||||||
|
if (draftCommentNotes == null ||
|
||||||
|
!author.equals(draftCommentNotes.getAuthor())) {
|
||||||
|
draftCommentNotes = new DraftCommentNotes(repoManager, allUsers,
|
||||||
|
getChangeId(), author);
|
||||||
|
draftCommentNotes.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsComment(PatchLineComment c) throws OrmException {
|
||||||
|
if (containsCommentPublished(c)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
loadDraftComments(c.getAuthor());
|
||||||
|
return draftCommentNotes.containsComment(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsCommentPublished(PatchLineComment c) {
|
||||||
|
PatchSet.Id psId = getCommentPsId(c);
|
||||||
|
List<PatchLineComment> list = (c.getSide() == (short) 0)
|
||||||
|
? getBaseComments().get(psId)
|
||||||
|
: getPatchSetComments().get(psId);
|
||||||
|
for (PatchLineComment l : list) {
|
||||||
|
if (c.getKey().equals(l.getKey())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return the NoteMap */
|
/** @return the NoteMap */
|
||||||
NoteMap getNoteMap() {
|
NoteMap getNoteMap() {
|
||||||
return noteMap;
|
return noteMap;
|
||||||
@@ -569,6 +647,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
reviewers.put(e.getValue(), e.getKey());
|
reviewers.put(e.getValue(), e.getKey());
|
||||||
}
|
}
|
||||||
this.reviewers = reviewers.build();
|
this.reviewers = reviewers.build();
|
||||||
|
this.allPastReviewers = ImmutableList.copyOf(parser.allPastReviewers);
|
||||||
|
|
||||||
submitRecords = ImmutableList.copyOf(parser.submitRecords);
|
submitRecords = ImmutableList.copyOf(parser.submitRecords);
|
||||||
} catch (ParseException e1) {
|
} catch (ParseException e1) {
|
||||||
|
@@ -33,9 +33,12 @@ import com.google.gerrit.reviewdb.client.Change;
|
|||||||
import com.google.gerrit.reviewdb.client.PatchLineComment;
|
import com.google.gerrit.reviewdb.client.PatchLineComment;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
|
||||||
import com.google.gerrit.server.GerritPersonIdent;
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.inject.Provider;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
@@ -60,12 +63,14 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single delta to apply atomically to a change.
|
* A delta to apply to a change.
|
||||||
* <p>
|
* <p>
|
||||||
* This delta becomes a single commit on the notes branch, so there are
|
* This delta will become two unique commits: one in the AllUsers repo that will
|
||||||
* limitations on the set of modifications that can be handled in a single
|
* contain the draft comments on this change and one in the notes branch that
|
||||||
* update. In particular, there is a single author and timestamp for each
|
* will contain approvals, reviewers, change status, subject, submit records,
|
||||||
* update.
|
* the change message, and published comments. There are limitations on the set
|
||||||
|
* of modifications that can be handled in a single update. In particular, there
|
||||||
|
* is a single author and timestamp for each update.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is not thread-safe.
|
* This class is not thread-safe.
|
||||||
*/
|
*/
|
||||||
@@ -88,6 +93,10 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
private List<PatchLineComment> commentsForBase;
|
private List<PatchLineComment> commentsForBase;
|
||||||
private List<PatchLineComment> commentsForPs;
|
private List<PatchLineComment> commentsForPs;
|
||||||
private String changeMessage;
|
private String changeMessage;
|
||||||
|
private ChangeNotes notes;
|
||||||
|
|
||||||
|
private final ChangeDraftUpdate.Factory draftUpdateFactory;
|
||||||
|
private ChangeDraftUpdate draftUpdate;
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
private ChangeUpdate(
|
private ChangeUpdate(
|
||||||
@@ -96,12 +105,16 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
NotesMigration migration,
|
NotesMigration migration,
|
||||||
AccountCache accountCache,
|
AccountCache accountCache,
|
||||||
MetaDataUpdate.User updateFactory,
|
MetaDataUpdate.User updateFactory,
|
||||||
|
DraftCommentNotes.Factory draftNotesFactory,
|
||||||
|
Provider<AllUsersName> allUsers,
|
||||||
|
ChangeDraftUpdate.Factory draftUpdateFactory,
|
||||||
ProjectCache projectCache,
|
ProjectCache projectCache,
|
||||||
IdentifiedUser user,
|
IdentifiedUser user,
|
||||||
@Assisted ChangeControl ctl,
|
@Assisted ChangeControl ctl,
|
||||||
CommentsInNotesUtil commentsUtil) {
|
CommentsInNotesUtil commentsUtil) {
|
||||||
this(serverIdent, repoManager, migration, accountCache, updateFactory,
|
this(serverIdent, repoManager, migration, accountCache, updateFactory,
|
||||||
projectCache, ctl, serverIdent.getWhen(), commentsUtil);
|
draftNotesFactory, allUsers, draftUpdateFactory, projectCache, ctl,
|
||||||
|
serverIdent.getWhen(), commentsUtil);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
@@ -111,12 +124,15 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
NotesMigration migration,
|
NotesMigration migration,
|
||||||
AccountCache accountCache,
|
AccountCache accountCache,
|
||||||
MetaDataUpdate.User updateFactory,
|
MetaDataUpdate.User updateFactory,
|
||||||
|
DraftCommentNotes.Factory draftNotesFactory,
|
||||||
|
Provider<AllUsersName> allUsers,
|
||||||
|
ChangeDraftUpdate.Factory draftUpdateFactory,
|
||||||
ProjectCache projectCache,
|
ProjectCache projectCache,
|
||||||
@Assisted ChangeControl ctl,
|
@Assisted ChangeControl ctl,
|
||||||
@Assisted Date when,
|
@Assisted Date when,
|
||||||
CommentsInNotesUtil commentsUtil) {
|
CommentsInNotesUtil commentsUtil) {
|
||||||
this(serverIdent, repoManager, migration, accountCache, updateFactory, ctl,
|
this(serverIdent, repoManager, migration, accountCache, updateFactory,
|
||||||
when,
|
draftNotesFactory, allUsers, draftUpdateFactory, ctl, when,
|
||||||
projectCache.get(getProjectName(ctl)).getLabelTypes().nameComparator(),
|
projectCache.get(getProjectName(ctl)).getLabelTypes().nameComparator(),
|
||||||
commentsUtil);
|
commentsUtil);
|
||||||
}
|
}
|
||||||
@@ -132,11 +148,15 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
NotesMigration migration,
|
NotesMigration migration,
|
||||||
AccountCache accountCache,
|
AccountCache accountCache,
|
||||||
MetaDataUpdate.User updateFactory,
|
MetaDataUpdate.User updateFactory,
|
||||||
|
DraftCommentNotes.Factory draftNotesFactory,
|
||||||
|
Provider<AllUsersName> allUsers,
|
||||||
|
ChangeDraftUpdate.Factory draftUpdateFactory,
|
||||||
@Assisted ChangeControl ctl,
|
@Assisted ChangeControl ctl,
|
||||||
@Assisted Date when,
|
@Assisted Date when,
|
||||||
@Assisted Comparator<String> labelNameComparator,
|
@Assisted Comparator<String> labelNameComparator,
|
||||||
CommentsInNotesUtil commentsUtil) {
|
CommentsInNotesUtil commentsUtil) {
|
||||||
super(migration, repoManager, updateFactory, ctl, serverIdent, when);
|
super(migration, repoManager, updateFactory, ctl, serverIdent, when);
|
||||||
|
this.draftUpdateFactory = draftUpdateFactory;
|
||||||
this.accountCache = accountCache;
|
this.accountCache = accountCache;
|
||||||
this.commentsUtil = commentsUtil;
|
this.commentsUtil = commentsUtil;
|
||||||
this.approvals = Maps.newTreeMap(labelNameComparator);
|
this.approvals = Maps.newTreeMap(labelNameComparator);
|
||||||
@@ -174,20 +194,132 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
this.changeMessage = changeMessage;
|
this.changeMessage = changeMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putComment(PatchLineComment comment) {
|
public void insertComment(PatchLineComment comment) throws OrmException {
|
||||||
checkArgument(psId != null,
|
if (comment.getStatus() == Status.DRAFT) {
|
||||||
"setPatchSetId must be called before putComment");
|
insertDraftComment(comment);
|
||||||
checkArgument(getCommentPsId(comment).equals(psId),
|
|
||||||
"Comment on %s doesn't match previous patch set %s",
|
|
||||||
getCommentPsId(comment), psId);
|
|
||||||
checkArgument(comment.getRevId() != null);
|
|
||||||
if (comment.getSide() == 0) {
|
|
||||||
commentsForBase.add(comment);
|
|
||||||
} else {
|
} else {
|
||||||
commentsForPs.add(comment);
|
insertPublishedComment(comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void upsertComment(PatchLineComment comment) throws OrmException {
|
||||||
|
if (comment.getStatus() == Status.DRAFT) {
|
||||||
|
upsertDraftComment(comment);
|
||||||
|
} else {
|
||||||
|
deleteDraftCommentIfPresent(comment);
|
||||||
|
upsertPublishedComment(comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateComment(PatchLineComment comment) throws OrmException {
|
||||||
|
if (comment.getStatus() == Status.DRAFT) {
|
||||||
|
updateDraftComment(comment);
|
||||||
|
} else {
|
||||||
|
deleteDraftCommentIfPresent(comment);
|
||||||
|
updatePublishedComment(comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteComment(PatchLineComment comment) throws OrmException {
|
||||||
|
if (comment.getStatus() == Status.DRAFT) {
|
||||||
|
deleteDraftComment(comment);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Cannot delete a published comment.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertPublishedComment(PatchLineComment c) throws OrmException {
|
||||||
|
verifyComment(c);
|
||||||
|
if (notes == null) {
|
||||||
|
notes = getChangeNotes().load();
|
||||||
|
}
|
||||||
|
checkArgument(!notes.containsComment(c),
|
||||||
|
"A comment already exists with the same key as the following comment,"
|
||||||
|
+ " so we cannot insert this comment: %s", c);
|
||||||
|
if (c.getSide() == 0) {
|
||||||
|
commentsForBase.add(c);
|
||||||
|
} else {
|
||||||
|
commentsForPs.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertDraftComment(PatchLineComment c) throws OrmException {
|
||||||
|
createDraftUpdateIfNull(c);
|
||||||
|
draftUpdate.insertComment(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upsertPublishedComment(PatchLineComment c) {
|
||||||
|
verifyComment(c);
|
||||||
|
if (c.getSide() == 0) {
|
||||||
|
commentsForBase.add(c);
|
||||||
|
} else {
|
||||||
|
commentsForPs.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upsertDraftComment(PatchLineComment c) throws OrmException {
|
||||||
|
createDraftUpdateIfNull(c);
|
||||||
|
draftUpdate.upsertComment(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePublishedComment(PatchLineComment c) throws OrmException {
|
||||||
|
verifyComment(c);
|
||||||
|
if (notes == null) {
|
||||||
|
notes = getChangeNotes().load();
|
||||||
|
}
|
||||||
|
checkArgument(!notes.containsCommentPublished(c),
|
||||||
|
"Cannot update a comment that has already been published and saved");
|
||||||
|
if (c.getSide() == 0) {
|
||||||
|
commentsForBase.add(c);
|
||||||
|
} else {
|
||||||
|
commentsForPs.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDraftComment(PatchLineComment c) throws OrmException {
|
||||||
|
createDraftUpdateIfNull(c);
|
||||||
|
draftUpdate.updateComment(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteDraftComment(PatchLineComment c) throws OrmException {
|
||||||
|
createDraftUpdateIfNull(c);
|
||||||
|
draftUpdate.deleteComment(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteDraftCommentIfPresent(PatchLineComment c)
|
||||||
|
throws OrmException {
|
||||||
|
createDraftUpdateIfNull(c);
|
||||||
|
draftUpdate.deleteCommentIfPresent(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createDraftUpdateIfNull(PatchLineComment c) throws OrmException {
|
||||||
|
if (draftUpdate == null) {
|
||||||
|
draftUpdate = draftUpdateFactory.create(ctl, when);
|
||||||
|
if (psId != null) {
|
||||||
|
draftUpdate.setPatchSetId(psId);
|
||||||
|
} else {
|
||||||
|
draftUpdate.setPatchSetId(getCommentPsId(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyComment(PatchLineComment c) {
|
||||||
|
checkArgument(psId != null,
|
||||||
|
"setPatchSetId must be called first");
|
||||||
|
checkArgument(getCommentPsId(c).equals(psId),
|
||||||
|
"Comment on %s doesn't match previous patch set %s",
|
||||||
|
getCommentPsId(c), psId);
|
||||||
|
checkArgument(c.getRevId() != null);
|
||||||
|
checkArgument(c.getStatus() == Status.PUBLISHED,
|
||||||
|
"Cannot add a draft comment to a ChangeUpdate. Use a ChangeDraftUpdate"
|
||||||
|
+ " for draft comments");
|
||||||
|
checkArgument(c.getAuthor().equals(getUser().getAccountId()),
|
||||||
|
"The author for the following comment does not match the author of"
|
||||||
|
+ " this ChangeDraftUpdate (%s): %s", getUser().getAccountId(), c);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void putReviewer(Account.Id reviewer, ReviewerState type) {
|
public void putReviewer(Account.Id reviewer, ReviewerState type) {
|
||||||
checkArgument(type != ReviewerState.REMOVED, "invalid ReviewerType");
|
checkArgument(type != ReviewerState.REMOVED, "invalid ReviewerType");
|
||||||
reviewers.put(reviewer, type);
|
reviewers.put(reviewer, type);
|
||||||
@@ -243,6 +375,9 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
batch.write(builder);
|
batch.write(builder);
|
||||||
|
if (draftUpdate != null) {
|
||||||
|
draftUpdate.commit();
|
||||||
|
}
|
||||||
RevCommit c = batch.commit();
|
RevCommit c = batch.commit();
|
||||||
return c;
|
return c;
|
||||||
} catch (OrmException e) {
|
} catch (OrmException e) {
|
||||||
|
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright (C) 2014 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.notedb;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.HashBasedTable;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchLineComment;
|
||||||
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.notes.NoteMap;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View of the draft comments for a single {@link Change} based on the log of
|
||||||
|
* its drafts branch.
|
||||||
|
*/
|
||||||
|
public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
|
||||||
|
@Singleton
|
||||||
|
public static class Factory {
|
||||||
|
private final GitRepositoryManager repoManager;
|
||||||
|
private final AllUsersName draftsProject;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@Inject
|
||||||
|
public Factory(GitRepositoryManager repoManager,
|
||||||
|
AllUsersNameProvider allUsers) {
|
||||||
|
this.repoManager = repoManager;
|
||||||
|
this.draftsProject = allUsers.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DraftCommentNotes create(Change.Id changeId, Account.Id accountId) {
|
||||||
|
return new DraftCommentNotes(repoManager, draftsProject, changeId,
|
||||||
|
accountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Parser {
|
||||||
|
private final Change.Id changeId;
|
||||||
|
private final ObjectId tip;
|
||||||
|
private final RevWalk walk;
|
||||||
|
private final Repository repo;
|
||||||
|
private final Account.Id author;
|
||||||
|
|
||||||
|
private final Multimap<PatchSet.Id, PatchLineComment> draftBaseComments;
|
||||||
|
private final Multimap<PatchSet.Id, PatchLineComment> draftPsComments;
|
||||||
|
private NoteMap noteMap;
|
||||||
|
|
||||||
|
private Parser(Change.Id changeId, RevWalk walk, ObjectId tip,
|
||||||
|
GitRepositoryManager repoManager, AllUsersName draftsProject,
|
||||||
|
Account.Id author) throws RepositoryNotFoundException, IOException {
|
||||||
|
this.changeId = changeId;
|
||||||
|
this.walk = walk;
|
||||||
|
this.tip = tip;
|
||||||
|
this.repo = repoManager.openRepository(draftsProject);
|
||||||
|
this.author = author;
|
||||||
|
|
||||||
|
draftBaseComments = ArrayListMultimap.create();
|
||||||
|
draftPsComments = ArrayListMultimap.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseDraftComments() throws IOException, ConfigInvalidException {
|
||||||
|
walk.markStart(walk.parseCommit(tip));
|
||||||
|
noteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo,
|
||||||
|
RefNames.refsDraftComments(author, changeId),
|
||||||
|
walk, changeId, draftBaseComments,
|
||||||
|
draftPsComments, Status.DRAFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AllUsersName draftsProject;
|
||||||
|
private final Account.Id author;
|
||||||
|
|
||||||
|
private final Table<PatchSet.Id, String, PatchLineComment> draftBaseComments;
|
||||||
|
private final Table<PatchSet.Id, String, PatchLineComment> draftPsComments;
|
||||||
|
private NoteMap noteMap;
|
||||||
|
|
||||||
|
DraftCommentNotes(GitRepositoryManager repoManager,
|
||||||
|
AllUsersName draftsProject, Change.Id changeId, Account.Id author) {
|
||||||
|
super(repoManager, changeId);
|
||||||
|
this.draftsProject = draftsProject;
|
||||||
|
this.author = author;
|
||||||
|
|
||||||
|
this.draftBaseComments = HashBasedTable.create();
|
||||||
|
this.draftPsComments = HashBasedTable.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoteMap getNoteMap() {
|
||||||
|
return noteMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account.Id getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a defensive copy of the table containing all draft comments
|
||||||
|
* on this change with side == 0. The row key is the comment's PatchSet.Id,
|
||||||
|
* the column key is the comment's UUID, and the value is the comment.
|
||||||
|
*/
|
||||||
|
public Table<PatchSet.Id, String, PatchLineComment>
|
||||||
|
getDraftBaseComments() {
|
||||||
|
return HashBasedTable.create(draftBaseComments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a defensive copy of the table containing all draft comments
|
||||||
|
* on this change with side == 1. The row key is the comment's PatchSet.Id,
|
||||||
|
* the column key is the comment's UUID, and the value is the comment.
|
||||||
|
*/
|
||||||
|
public Table<PatchSet.Id, String, PatchLineComment>
|
||||||
|
getDraftPsComments() {
|
||||||
|
return HashBasedTable.create(draftPsComments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsComment(PatchLineComment c) {
|
||||||
|
Table<PatchSet.Id, String, PatchLineComment> t =
|
||||||
|
c.getSide() == (short) 0
|
||||||
|
? getDraftBaseComments()
|
||||||
|
: getDraftPsComments();
|
||||||
|
return t.contains(getCommentPsId(c), c.getKey().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getRefName() {
|
||||||
|
return RefNames.refsDraftComments(author, getChangeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||||
|
ObjectId rev = getRevision();
|
||||||
|
if (rev == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RevWalk walk = new RevWalk(reader);
|
||||||
|
Parser parser = new Parser(getChangeId(), walk, rev, repoManager,
|
||||||
|
draftsProject, author);
|
||||||
|
parser.parseDraftComments();
|
||||||
|
|
||||||
|
buildCommentTable(draftBaseComments, parser.draftBaseComments);
|
||||||
|
buildCommentTable(draftPsComments, parser.draftPsComments);
|
||||||
|
noteMap = parser.noteMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onSave(CommitBuilder commit) throws IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
getClass().getSimpleName() + " is read-only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Project.NameKey getProjectName() {
|
||||||
|
return draftsProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildCommentTable(
|
||||||
|
Table<PatchSet.Id, String, PatchLineComment> commentTable,
|
||||||
|
Multimap<PatchSet.Id, PatchLineComment> allComments) {
|
||||||
|
for (PatchLineComment c : allComments.values()) {
|
||||||
|
commentTable.put(getCommentPsId(c), c.getKey().get(), c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,5 +20,6 @@ public class NoteDbModule extends FactoryModule {
|
|||||||
@Override
|
@Override
|
||||||
public void configure() {
|
public void configure() {
|
||||||
factory(ChangeUpdate.Factory.class);
|
factory(ChangeUpdate.Factory.class);
|
||||||
|
factory(ChangeDraftUpdate.Factory.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,6 +50,7 @@ import com.google.gerrit.server.account.AccountCache;
|
|||||||
import com.google.gerrit.server.account.AccountInfo;
|
import com.google.gerrit.server.account.AccountInfo;
|
||||||
import com.google.gerrit.server.account.CapabilityControl;
|
import com.google.gerrit.server.account.CapabilityControl;
|
||||||
import com.google.gerrit.server.account.GroupBackend;
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
|
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||||
import com.google.gerrit.server.config.AnonymousCowardName;
|
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||||
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
@@ -110,6 +111,7 @@ public class CommentsTest {
|
|||||||
private Injector injector;
|
private Injector injector;
|
||||||
private Project.NameKey project;
|
private Project.NameKey project;
|
||||||
private InMemoryRepositoryManager repoManager;
|
private InMemoryRepositoryManager repoManager;
|
||||||
|
private AllUsersNameProvider allUsers;
|
||||||
private PatchLineCommentsUtil plcUtil;
|
private PatchLineCommentsUtil plcUtil;
|
||||||
private RevisionResource revRes1;
|
private RevisionResource revRes1;
|
||||||
private RevisionResource revRes2;
|
private RevisionResource revRes2;
|
||||||
@@ -165,6 +167,9 @@ public class CommentsTest {
|
|||||||
injector = Guice.createInjector(mod);
|
injector = Guice.createInjector(mod);
|
||||||
|
|
||||||
NotesMigration migration = injector.getInstance(NotesMigration.class);
|
NotesMigration migration = injector.getInstance(NotesMigration.class);
|
||||||
|
allUsers = injector.getInstance(AllUsersNameProvider.class);
|
||||||
|
repoManager.createRepository(allUsers.get());
|
||||||
|
|
||||||
plcUtil = new PatchLineCommentsUtil(migration);
|
plcUtil = new PatchLineCommentsUtil(migration);
|
||||||
|
|
||||||
Account co = new Account(new Account.Id(1), TimeUtil.nowTs());
|
Account co = new Account(new Account.Id(1), TimeUtil.nowTs());
|
||||||
@@ -250,7 +255,7 @@ public class CommentsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ChangeControl stubChangeControl(Change c) throws OrmException {
|
private ChangeControl stubChangeControl(Change c) throws OrmException {
|
||||||
return TestChanges.stubChangeControl(repoManager, c, changeOwner);
|
return TestChanges.stubChangeControl(repoManager, c, allUsers, changeOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Change newChange() {
|
private Change newChange() {
|
||||||
@@ -258,7 +263,7 @@ public class CommentsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ChangeUpdate newUpdate(Change c, final IdentifiedUser user) throws Exception {
|
private ChangeUpdate newUpdate(Change c, final IdentifiedUser user) throws Exception {
|
||||||
return TestChanges.newUpdate(injector, repoManager, c, user);
|
return TestChanges.newUpdate(injector, repoManager, c, allUsers, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -49,6 +49,7 @@ import com.google.gerrit.server.account.AccountCache;
|
|||||||
import com.google.gerrit.server.account.CapabilityControl;
|
import com.google.gerrit.server.account.CapabilityControl;
|
||||||
import com.google.gerrit.server.account.GroupBackend;
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
import com.google.gerrit.server.account.Realm;
|
import com.google.gerrit.server.account.Realm;
|
||||||
|
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||||
import com.google.gerrit.server.config.AnonymousCowardName;
|
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||||
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
@@ -88,6 +89,7 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -104,8 +106,10 @@ public class ChangeNotesTest {
|
|||||||
private InMemoryRepositoryManager repoManager;
|
private InMemoryRepositoryManager repoManager;
|
||||||
private InMemoryRepository repo;
|
private InMemoryRepository repo;
|
||||||
private FakeAccountCache accountCache;
|
private FakeAccountCache accountCache;
|
||||||
|
private AllUsersNameProvider allUsers;
|
||||||
private IdentifiedUser changeOwner;
|
private IdentifiedUser changeOwner;
|
||||||
private IdentifiedUser otherUser;
|
private IdentifiedUser otherUser;
|
||||||
|
private Account.Id otherUserId;
|
||||||
private Injector injector;
|
private Injector injector;
|
||||||
private String systemTimeZone;
|
private String systemTimeZone;
|
||||||
private volatile long clockStepMs;
|
private volatile long clockStepMs;
|
||||||
@@ -157,8 +161,11 @@ public class ChangeNotesTest {
|
|||||||
|
|
||||||
IdentifiedUser.GenericFactory userFactory =
|
IdentifiedUser.GenericFactory userFactory =
|
||||||
injector.getInstance(IdentifiedUser.GenericFactory.class);
|
injector.getInstance(IdentifiedUser.GenericFactory.class);
|
||||||
|
allUsers = injector.getInstance(AllUsersNameProvider.class);
|
||||||
|
repoManager.createRepository(allUsers.get());
|
||||||
changeOwner = userFactory.create(co.getId());
|
changeOwner = userFactory.create(co.getId());
|
||||||
otherUser = userFactory.create(ou.getId());
|
otherUser = userFactory.create(ou.getId());
|
||||||
|
otherUserId = otherUser.getAccountId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTimeForTesting() {
|
private void setTimeForTesting() {
|
||||||
@@ -858,7 +865,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range1, range1.getEndLine(), otherUser, null, time1, message1,
|
uuid, range1, range1.getEndLine(), otherUser, null, time1, message1,
|
||||||
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment1);
|
update.upsertComment(comment1);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
update = newUpdate(c, otherUser);
|
update = newUpdate(c, otherUser);
|
||||||
@@ -867,7 +874,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range2, range2.getEndLine(), otherUser, null, time2, message2,
|
uuid, range2, range2.getEndLine(), otherUser, null, time2, message2,
|
||||||
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment2);
|
update.upsertComment(comment2);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
update = newUpdate(c, otherUser);
|
update = newUpdate(c, otherUser);
|
||||||
@@ -876,7 +883,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range3, range3.getEndLine(), otherUser, null, time3, message3,
|
uuid, range3, range3.getEndLine(), otherUser, null, time3, message3,
|
||||||
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment3);
|
update.upsertComment(comment3);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
ChangeNotes notes = newNotes(c);
|
ChangeNotes notes = newNotes(c);
|
||||||
@@ -936,7 +943,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range1, range1.getEndLine(), otherUser, null, time1, message1,
|
uuid, range1, range1.getEndLine(), otherUser, null, time1, message1,
|
||||||
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment1);
|
update.upsertComment(comment1);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
update = newUpdate(c, otherUser);
|
update = newUpdate(c, otherUser);
|
||||||
@@ -945,7 +952,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range2, range2.getEndLine(), otherUser, null, time2, message2,
|
uuid, range2, range2.getEndLine(), otherUser, null, time2, message2,
|
||||||
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment2);
|
update.upsertComment(comment2);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
ChangeNotes notes = newNotes(c);
|
ChangeNotes notes = newNotes(c);
|
||||||
@@ -998,7 +1005,7 @@ public class ChangeNotesTest {
|
|||||||
range, range.getEndLine(), otherUser, null, now, messageForBase,
|
range, range.getEndLine(), otherUser, null, now, messageForBase,
|
||||||
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(commentForBase);
|
update.upsertComment(commentForBase);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
update = newUpdate(c, otherUser);
|
update = newUpdate(c, otherUser);
|
||||||
@@ -1007,7 +1014,7 @@ public class ChangeNotesTest {
|
|||||||
range, range.getEndLine(), otherUser, null, now, messageForPS,
|
range, range.getEndLine(), otherUser, null, now, messageForPS,
|
||||||
(short) 1, "abcd4567abcd4567abcd4567abcd4567abcd4567");
|
(short) 1, "abcd4567abcd4567abcd4567abcd4567abcd4567");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(commentForPS);
|
update.upsertComment(commentForPS);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
ChangeNotes notes = newNotes(c);
|
ChangeNotes notes = newNotes(c);
|
||||||
@@ -1040,7 +1047,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range, range.getEndLine(), otherUser, null, timeForComment1,
|
uuid, range, range.getEndLine(), otherUser, null, timeForComment1,
|
||||||
"comment 1", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
"comment 1", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment1);
|
update.upsertComment(comment1);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
update = newUpdate(c, otherUser);
|
update = newUpdate(c, otherUser);
|
||||||
@@ -1048,7 +1055,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range, range.getEndLine(), otherUser, null, timeForComment2,
|
uuid, range, range.getEndLine(), otherUser, null, timeForComment2,
|
||||||
"comment 2", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
"comment 2", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment2);
|
update.upsertComment(comment2);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
ChangeNotes notes = newNotes(c);
|
ChangeNotes notes = newNotes(c);
|
||||||
@@ -1086,7 +1093,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range, range.getEndLine(), otherUser, null, now, "comment 1",
|
uuid, range, range.getEndLine(), otherUser, null, now, "comment 1",
|
||||||
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment1);
|
update.upsertComment(comment1);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
update = newUpdate(c, otherUser);
|
update = newUpdate(c, otherUser);
|
||||||
@@ -1094,7 +1101,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range, range.getEndLine(), otherUser, null, now, "comment 2",
|
uuid, range, range.getEndLine(), otherUser, null, now, "comment 2",
|
||||||
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(psId);
|
update.setPatchSetId(psId);
|
||||||
update.putComment(comment2);
|
update.upsertComment(comment2);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
ChangeNotes notes = newNotes(c);
|
ChangeNotes notes = newNotes(c);
|
||||||
@@ -1130,7 +1137,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
|
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
|
||||||
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
|
||||||
update.setPatchSetId(ps1);
|
update.setPatchSetId(ps1);
|
||||||
update.putComment(comment1);
|
update.upsertComment(comment1);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
incrementPatchSet(c);
|
incrementPatchSet(c);
|
||||||
@@ -1142,7 +1149,7 @@ public class ChangeNotesTest {
|
|||||||
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
|
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
|
||||||
side, "abcd4567abcd4567abcd4567abcd4567abcd4567");
|
side, "abcd4567abcd4567abcd4567abcd4567abcd4567");
|
||||||
update.setPatchSetId(ps2);
|
update.setPatchSetId(ps2);
|
||||||
update.putComment(comment2);
|
update.upsertComment(comment2);
|
||||||
update.commit();
|
update.commit();
|
||||||
|
|
||||||
ChangeNotes notes = newNotes(c);
|
ChangeNotes notes = newNotes(c);
|
||||||
@@ -1165,6 +1172,160 @@ public class ChangeNotesTest {
|
|||||||
assertEquals(comment2, commentFromPs2);
|
assertEquals(comment2, commentFromPs2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchLineCommentSingleDraftToPublished() throws Exception {
|
||||||
|
Change c = newChange();
|
||||||
|
String uuid = "uuid";
|
||||||
|
CommentRange range = new CommentRange(1, 1, 2, 1);
|
||||||
|
PatchSet.Id ps1 = c.currentPatchSetId();
|
||||||
|
String filename = "filename1";
|
||||||
|
short side = (short) 1;
|
||||||
|
|
||||||
|
ChangeUpdate update = newUpdate(c, otherUser);
|
||||||
|
Timestamp now = TimeUtil.nowTs();
|
||||||
|
PatchLineComment comment1 = newPatchLineComment(ps1, filename, uuid,
|
||||||
|
range, range.getEndLine(), otherUser, null, now, "comment on ps1", side,
|
||||||
|
"abcd4567abcd4567abcd4567abcd4567abcd4567", Status.DRAFT);
|
||||||
|
update.setPatchSetId(ps1);
|
||||||
|
update.insertComment(comment1);
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
ChangeNotes notes = newNotes(c);
|
||||||
|
assertEquals(1, notes.getDraftPsComments(otherUserId).values().size());
|
||||||
|
assertEquals(0, notes.getDraftBaseComments(otherUserId).values().size());
|
||||||
|
|
||||||
|
comment1.setStatus(Status.PUBLISHED);
|
||||||
|
update = newUpdate(c, otherUser);
|
||||||
|
update.setPatchSetId(ps1);
|
||||||
|
update.updateComment(comment1);
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
notes = newNotes(c);
|
||||||
|
|
||||||
|
assertTrue(notes.getDraftPsComments(otherUserId).values().isEmpty());
|
||||||
|
assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty());
|
||||||
|
|
||||||
|
assertTrue(notes.getBaseComments().values().isEmpty());
|
||||||
|
PatchLineComment commentFromNotes =
|
||||||
|
Iterables.getOnlyElement(notes.getPatchSetComments().values());
|
||||||
|
assertEquals(comment1, commentFromNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchLineCommentMultipleDraftsSameSidePublishOne()
|
||||||
|
throws OrmException, IOException {
|
||||||
|
Change c = newChange();
|
||||||
|
String uuid1 = "uuid1";
|
||||||
|
String uuid2 = "uuid2";
|
||||||
|
CommentRange range1 = new CommentRange(1, 1, 2, 2);
|
||||||
|
CommentRange range2 = new CommentRange(2, 2, 3, 3);
|
||||||
|
String filename = "filename1";
|
||||||
|
short side = (short) 1;
|
||||||
|
Timestamp now = TimeUtil.nowTs();
|
||||||
|
String commitSHA1 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
|
||||||
|
PatchSet.Id psId = c.currentPatchSetId();
|
||||||
|
|
||||||
|
// Write two drafts on the same side of one patch set.
|
||||||
|
ChangeUpdate update = newUpdate(c, otherUser);
|
||||||
|
update.setPatchSetId(psId);
|
||||||
|
PatchLineComment comment1 = newPatchLineComment(psId, filename, uuid1,
|
||||||
|
range1, range1.getEndLine(), otherUser, null, now, "comment on ps1",
|
||||||
|
side, commitSHA1, Status.DRAFT);
|
||||||
|
PatchLineComment comment2 = newPatchLineComment(psId, filename, uuid2,
|
||||||
|
range2, range2.getEndLine(), otherUser, null, now, "other on ps1",
|
||||||
|
side, commitSHA1, Status.DRAFT);
|
||||||
|
update.insertComment(comment1);
|
||||||
|
update.insertComment(comment2);
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
ChangeNotes notes = newNotes(c);
|
||||||
|
assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty());
|
||||||
|
assertEquals(2, notes.getDraftPsComments(otherUserId).values().size());
|
||||||
|
|
||||||
|
assertTrue(notes.getDraftPsComments(otherUserId).containsValue(comment1));
|
||||||
|
assertTrue(notes.getDraftPsComments(otherUserId).containsValue(comment2));
|
||||||
|
|
||||||
|
// Publish first draft.
|
||||||
|
update = newUpdate(c, otherUser);
|
||||||
|
update.setPatchSetId(psId);
|
||||||
|
comment1.setStatus(Status.PUBLISHED);
|
||||||
|
update.updateComment(comment1);
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
notes = newNotes(c);
|
||||||
|
assertEquals(comment1,
|
||||||
|
Iterables.getOnlyElement(notes.getPatchSetComments().get(psId)));
|
||||||
|
assertEquals(comment2,
|
||||||
|
Iterables.getOnlyElement(
|
||||||
|
notes.getDraftPsComments(otherUserId).values()));
|
||||||
|
|
||||||
|
assertTrue(notes.getBaseComments().values().isEmpty());
|
||||||
|
assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchLineCommentsMultipleDraftsBothSidesPublishAll()
|
||||||
|
throws OrmException, IOException {
|
||||||
|
Change c = newChange();
|
||||||
|
String uuid1 = "uuid1";
|
||||||
|
String uuid2 = "uuid2";
|
||||||
|
CommentRange range1 = new CommentRange(1, 1, 2, 2);
|
||||||
|
CommentRange range2 = new CommentRange(2, 2, 3, 3);
|
||||||
|
String filename = "filename1";
|
||||||
|
Timestamp now = TimeUtil.nowTs();
|
||||||
|
String commitSHA1 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
|
||||||
|
String baseSHA1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
|
||||||
|
PatchSet.Id psId = c.currentPatchSetId();
|
||||||
|
|
||||||
|
// Write two drafts, one on each side of the patchset.
|
||||||
|
ChangeUpdate update = newUpdate(c, otherUser);
|
||||||
|
update.setPatchSetId(psId);
|
||||||
|
PatchLineComment baseComment = newPatchLineComment(psId, filename, uuid1,
|
||||||
|
range1, range1.getEndLine(), otherUser, null, now, "comment on base",
|
||||||
|
(short) 0, baseSHA1, Status.DRAFT);
|
||||||
|
PatchLineComment psComment = newPatchLineComment(psId, filename, uuid2,
|
||||||
|
range2, range2.getEndLine(), otherUser, null, now, "comment on ps",
|
||||||
|
(short) 1, commitSHA1, Status.DRAFT);
|
||||||
|
|
||||||
|
update.insertComment(baseComment);
|
||||||
|
update.insertComment(psComment);
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
ChangeNotes notes = newNotes(c);
|
||||||
|
PatchLineComment baseDraftCommentFromNotes =
|
||||||
|
Iterables.getOnlyElement(
|
||||||
|
notes.getDraftBaseComments(otherUserId).values());
|
||||||
|
PatchLineComment psDraftCommentFromNotes =
|
||||||
|
Iterables.getOnlyElement(
|
||||||
|
notes.getDraftPsComments(otherUserId).values());
|
||||||
|
|
||||||
|
assertEquals(baseComment, baseDraftCommentFromNotes);
|
||||||
|
assertEquals(psComment, psDraftCommentFromNotes);
|
||||||
|
|
||||||
|
// Publish both comments.
|
||||||
|
update = newUpdate(c, otherUser);
|
||||||
|
update.setPatchSetId(psId);
|
||||||
|
|
||||||
|
baseComment.setStatus(Status.PUBLISHED);
|
||||||
|
psComment.setStatus(Status.PUBLISHED);
|
||||||
|
update.updateComment(baseComment);
|
||||||
|
update.updateComment(psComment);
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
notes = newNotes(c);
|
||||||
|
|
||||||
|
PatchLineComment baseCommentFromNotes =
|
||||||
|
Iterables.getOnlyElement(notes.getBaseComments().values());
|
||||||
|
PatchLineComment psCommentFromNotes =
|
||||||
|
Iterables.getOnlyElement(notes.getPatchSetComments().values());
|
||||||
|
|
||||||
|
assertEquals(baseComment, baseCommentFromNotes);
|
||||||
|
assertEquals(psComment, psCommentFromNotes);
|
||||||
|
|
||||||
|
assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty());
|
||||||
|
assertTrue(notes.getDraftPsComments(otherUserId).values().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
private Change newChange() {
|
private Change newChange() {
|
||||||
return TestChanges.newChange(project, changeOwner);
|
return TestChanges.newChange(project, changeOwner);
|
||||||
}
|
}
|
||||||
@@ -1194,12 +1355,12 @@ public class ChangeNotesTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ChangeUpdate newUpdate(Change c, IdentifiedUser user)
|
private ChangeUpdate newUpdate(Change c, IdentifiedUser user)
|
||||||
throws Exception {
|
throws OrmException {
|
||||||
return TestChanges.newUpdate(injector, repoManager, c, user);
|
return TestChanges.newUpdate(injector, repoManager, c, allUsers, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChangeNotes newNotes(Change c) throws OrmException {
|
private ChangeNotes newNotes(Change c) throws OrmException {
|
||||||
return new ChangeNotes(repoManager, c).load();
|
return new ChangeNotes(repoManager, allUsers, c).load();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Timestamp truncate(Timestamp ts) {
|
private static Timestamp truncate(Timestamp ts) {
|
||||||
|
@@ -23,8 +23,11 @@ import com.google.gerrit.reviewdb.client.PatchSet;
|
|||||||
import com.google.gerrit.reviewdb.client.PatchSetInfo;
|
import com.google.gerrit.reviewdb.client.PatchSetInfo;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||||
import com.google.gerrit.server.config.FactoryModule;
|
import com.google.gerrit.server.config.FactoryModule;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeDraftUpdate;
|
||||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||||
import com.google.gerrit.server.notedb.ChangeUpdate;
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
@@ -52,26 +55,29 @@ public class TestChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ChangeUpdate newUpdate(Injector injector,
|
public static ChangeUpdate newUpdate(Injector injector,
|
||||||
GitRepositoryManager repoManager, Change c, final IdentifiedUser user)
|
GitRepositoryManager repoManager, Change c,
|
||||||
|
final AllUsersNameProvider allUsers, final IdentifiedUser user)
|
||||||
throws OrmException {
|
throws OrmException {
|
||||||
return injector.createChildInjector(new FactoryModule() {
|
return injector.createChildInjector(new FactoryModule() {
|
||||||
@Override
|
@Override
|
||||||
public void configure() {
|
public void configure() {
|
||||||
factory(ChangeUpdate.Factory.class);
|
factory(ChangeUpdate.Factory.class);
|
||||||
|
factory(ChangeDraftUpdate.Factory.class);
|
||||||
bind(IdentifiedUser.class).toInstance(user);
|
bind(IdentifiedUser.class).toInstance(user);
|
||||||
|
bind(AllUsersName.class).toProvider(allUsers);
|
||||||
}
|
}
|
||||||
}).getInstance(ChangeUpdate.Factory.class).create(
|
}).getInstance(ChangeUpdate.Factory.class).create(
|
||||||
stubChangeControl(repoManager, c, user), TimeUtil.nowTs(),
|
stubChangeControl(repoManager, c, allUsers, user), TimeUtil.nowTs(),
|
||||||
Ordering.<String> natural());
|
Ordering.<String> natural());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChangeControl stubChangeControl(
|
public static ChangeControl stubChangeControl(
|
||||||
GitRepositoryManager repoManager, Change c, IdentifiedUser user)
|
GitRepositoryManager repoManager, Change c, AllUsersNameProvider allUsers,
|
||||||
throws OrmException {
|
IdentifiedUser user) throws OrmException {
|
||||||
ChangeControl ctl = EasyMock.createNiceMock(ChangeControl.class);
|
ChangeControl ctl = EasyMock.createNiceMock(ChangeControl.class);
|
||||||
expect(ctl.getChange()).andStubReturn(c);
|
expect(ctl.getChange()).andStubReturn(c);
|
||||||
expect(ctl.getCurrentUser()).andStubReturn(user);
|
expect(ctl.getCurrentUser()).andStubReturn(user);
|
||||||
ChangeNotes notes = new ChangeNotes(repoManager, c);
|
ChangeNotes notes = new ChangeNotes(repoManager, allUsers, c);
|
||||||
notes = notes.load();
|
notes = notes.load();
|
||||||
expect(ctl.getNotes()).andStubReturn(notes);
|
expect(ctl.getNotes()).andStubReturn(notes);
|
||||||
EasyMock.replay(ctl);
|
EasyMock.replay(ctl);
|
||||||
|
Reference in New Issue
Block a user