blob: d5242c2bd8e2087157baf5c1e4cb175e6983ed1e [file] [log] [blame]
// 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;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
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.PatchLineComment.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
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.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.DraftCommentNotes;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Utility functions to manipulate PatchLineComments.
* <p>
* These methods either query for and update PatchLineComments in the NoteDb or
* ReviewDb, depending on the state of the NotesMigration.
*/
@Singleton
public class PatchLineCommentsUtil {
private final GitRepositoryManager repoManager;
private final AllUsersName allUsers;
private final DraftCommentNotes.Factory draftFactory;
private final NotesMigration migration;
@VisibleForTesting
@Inject
public PatchLineCommentsUtil(GitRepositoryManager repoManager,
AllUsersNameProvider allUsersProvider,
DraftCommentNotes.Factory draftFactory,
NotesMigration migration) {
this.repoManager = repoManager;
this.allUsers = allUsersProvider.get();
this.draftFactory = draftFactory;
this.migration = migration;
}
public Optional<PatchLineComment> get(ReviewDb db, ChangeNotes notes,
PatchLineComment.Key key) throws OrmException {
if (!migration.readChanges()) {
return Optional.fromNullable(db.patchComments().get(key));
}
for (PatchLineComment c : publishedByChange(db, notes)) {
if (key.equals(c.getKey())) {
return Optional.of(c);
}
}
for (PatchLineComment c : draftByChange(db, notes)) {
if (key.equals(c.getKey())) {
return Optional.of(c);
}
}
return Optional.absent();
}
public List<PatchLineComment> publishedByChange(ReviewDb db,
ChangeNotes notes) throws OrmException {
if (!migration.readChanges()) {
return sort(byCommentStatus(
db.patchComments().byChange(notes.getChangeId()), Status.PUBLISHED));
}
notes.load();
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(notes.getBaseComments().values());
comments.addAll(notes.getPatchSetComments().values());
return sort(comments);
}
public List<PatchLineComment> draftByChange(ReviewDb db,
ChangeNotes notes) throws OrmException {
if (!migration.readChanges()) {
return sort(byCommentStatus(
db.patchComments().byChange(notes.getChangeId()), Status.DRAFT));
}
List<PatchLineComment> comments = Lists.newArrayList();
Iterable<String> filtered = getDraftRefs(notes.getChangeId());
for (String refName : filtered) {
Account.Id account = Account.Id.fromRefPart(refName);
if (account != null) {
comments.addAll(draftByChangeAuthor(db, notes, account));
}
}
return sort(comments);
}
private static List<PatchLineComment> byCommentStatus(
ResultSet<PatchLineComment> comments,
final PatchLineComment.Status status) {
return Lists.newArrayList(
Iterables.filter(comments, new Predicate<PatchLineComment>() {
@Override
public boolean apply(PatchLineComment input) {
return (input.getStatus() == status);
}
})
);
}
public List<PatchLineComment> byPatchSet(ReviewDb db,
ChangeNotes notes, PatchSet.Id psId) throws OrmException {
if (!migration.readChanges()) {
return sort(db.patchComments().byPatchSet(psId).toList());
}
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(publishedByPatchSet(db, notes, psId));
Iterable<String> filtered = getDraftRefs(notes.getChangeId());
for (String refName : filtered) {
Account.Id account = Account.Id.fromRefPart(refName);
if (account != null) {
comments.addAll(draftByPatchSetAuthor(db, psId, account, notes));
}
}
return sort(comments);
}
public List<PatchLineComment> publishedByChangeFile(ReviewDb db,
ChangeNotes notes, Change.Id changeId, String file) throws OrmException {
if (!migration.readChanges()) {
return sort(
db.patchComments().publishedByChangeFile(changeId, file).toList());
}
notes.load();
List<PatchLineComment> comments = Lists.newArrayList();
addCommentsOnFile(comments, notes.getBaseComments().values(), file);
addCommentsOnFile(comments, notes.getPatchSetComments().values(),
file);
return sort(comments);
}
public List<PatchLineComment> publishedByPatchSet(ReviewDb db,
ChangeNotes notes, PatchSet.Id psId) throws OrmException {
if (!migration.readChanges()) {
return sort(
db.patchComments().publishedByPatchSet(psId).toList());
}
notes.load();
List<PatchLineComment> comments = new ArrayList<>();
comments.addAll(notes.getPatchSetComments().get(psId));
comments.addAll(notes.getBaseComments().get(psId));
return sort(comments);
}
public List<PatchLineComment> draftByPatchSetAuthor(ReviewDb db,
PatchSet.Id psId, Account.Id author, ChangeNotes notes)
throws OrmException {
if (!migration.readChanges()) {
return sort(
db.patchComments().draftByPatchSetAuthor(psId, author).toList());
}
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(notes.getDraftBaseComments(author).row(psId).values());
comments.addAll(notes.getDraftPsComments(author).row(psId).values());
return sort(comments);
}
public List<PatchLineComment> draftByChangeFileAuthor(ReviewDb db,
ChangeNotes notes, String file, Account.Id author)
throws OrmException {
if (!migration.readChanges()) {
return sort(
db.patchComments()
.draftByChangeFileAuthor(notes.getChangeId(), file, author)
.toList());
}
List<PatchLineComment> comments = Lists.newArrayList();
addCommentsOnFile(comments, notes.getDraftBaseComments(author).values(),
file);
addCommentsOnFile(comments, notes.getDraftPsComments(author).values(),
file);
return sort(comments);
}
public List<PatchLineComment> draftByChangeAuthor(ReviewDb db,
ChangeNotes notes, Account.Id author)
throws OrmException {
if (!migration.readChanges()) {
final Change.Id matchId = notes.getChangeId();
return FluentIterable
.from(db.patchComments().draftByAuthor(author))
.filter(new Predicate<PatchLineComment>() {
@Override
public boolean apply(PatchLineComment in) {
Change.Id changeId =
in.getKey().getParentKey().getParentKey().getParentKey();
return changeId.equals(matchId);
}
}).toSortedList(ChangeNotes.PLC_ORDER);
}
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(notes.getDraftBaseComments(author).values());
comments.addAll(notes.getDraftPsComments(author).values());
return sort(comments);
}
public List<PatchLineComment> draftByAuthor(ReviewDb db,
Account.Id author) throws OrmException {
if (!migration.readChanges()) {
return sort(db.patchComments().draftByAuthor(author).toList());
}
Set<String> refNames =
getRefNamesAllUsers(RefNames.REFS_DRAFT_COMMENTS);
List<PatchLineComment> comments = Lists.newArrayList();
for (String refName : refNames) {
Account.Id id = Account.Id.fromRefPart(refName);
if (!author.equals(id)) {
continue;
}
Change.Id changeId = Change.Id.parse(refName);
DraftCommentNotes draftNotes =
draftFactory.create(changeId, author).load();
comments.addAll(draftNotes.getDraftBaseComments().values());
comments.addAll(draftNotes.getDraftPsComments().values());
}
return sort(comments);
}
public void insertComments(ReviewDb db, ChangeUpdate update,
Iterable<PatchLineComment> comments) throws OrmException {
for (PatchLineComment c : comments) {
update.insertComment(c);
}
db.patchComments().insert(comments);
}
public void upsertComments(ReviewDb db, ChangeUpdate update,
Iterable<PatchLineComment> comments) throws OrmException {
for (PatchLineComment c : comments) {
update.upsertComment(c);
}
db.patchComments().upsert(comments);
}
public void updateComments(ReviewDb db, ChangeUpdate update,
Iterable<PatchLineComment> comments) throws OrmException {
for (PatchLineComment c : comments) {
update.updateComment(c);
}
db.patchComments().update(comments);
}
public void deleteComments(ReviewDb db, ChangeUpdate update,
Iterable<PatchLineComment> comments) throws OrmException {
for (PatchLineComment c : comments) {
update.deleteComment(c);
}
db.patchComments().delete(comments);
}
private static Collection<PatchLineComment> addCommentsOnFile(
Collection<PatchLineComment> commentsOnFile,
Collection<PatchLineComment> allComments,
String file) {
for (PatchLineComment c : allComments) {
String currentFilename = c.getKey().getParentKey().getFileName();
if (currentFilename.equals(file)) {
commentsOnFile.add(c);
}
}
return commentsOnFile;
}
public static void setCommentRevId(PatchLineComment c,
PatchListCache cache, Change change, PatchSet ps) throws OrmException {
if (c.getRevId() != null) {
return;
}
PatchList patchList;
try {
patchList = cache.get(change, ps);
} catch (PatchListNotAvailableException e) {
throw new OrmException(e);
}
c.setRevId((c.getSide() == (short) 0)
? new RevId(ObjectId.toString(patchList.getOldId()))
: new RevId(ObjectId.toString(patchList.getNewId())));
}
private Set<String> getRefNamesAllUsers(String prefix) throws OrmException {
Repository repo;
try {
repo = repoManager.openRepository(allUsers);
} catch (IOException e) {
throw new OrmException(e);
}
try {
RefDatabase refDb = repo.getRefDatabase();
return refDb.getRefs(prefix).keySet();
} catch (IOException e) {
throw new OrmException(e);
} finally {
repo.close();
}
}
private Iterable<String> getDraftRefs(final Change.Id changeId)
throws OrmException {
Set<String> refNames = getRefNamesAllUsers(RefNames.REFS_DRAFT_COMMENTS);
final String suffix = "-" + changeId.get();
return Iterables.filter(refNames, new Predicate<String>() {
@Override
public boolean apply(String input) {
return input.endsWith(suffix);
}
});
}
private static List<PatchLineComment> sort(List<PatchLineComment> comments) {
Collections.sort(comments, ChangeNotes.PLC_ORDER);
return comments;
}
}