| // Copyright (C) 2010 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.server.git; |
| |
| import static com.google.gerrit.server.git.GitRepositoryManager.REFS_NOTES_REVIEW; |
| |
| import com.google.gerrit.common.data.ApprovalType; |
| import com.google.gerrit.common.data.ApprovalTypes; |
| import com.google.gerrit.reviewdb.client.ApprovalCategory; |
| import com.google.gerrit.reviewdb.client.Change; |
| import com.google.gerrit.reviewdb.client.PatchSet; |
| import com.google.gerrit.reviewdb.client.PatchSetApproval; |
| import com.google.gerrit.reviewdb.server.ReviewDb; |
| import com.google.gerrit.server.GerritPersonIdent; |
| import com.google.gerrit.server.account.AccountCache; |
| import com.google.gerrit.server.config.AnonymousCowardName; |
| import com.google.gerrit.server.config.CanonicalWebUrl; |
| import com.google.gwtorm.server.OrmException; |
| import com.google.gwtorm.server.ResultSet; |
| import com.google.inject.Inject; |
| import com.google.inject.assistedinject.Assisted; |
| |
| import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.NullProgressMonitor; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.ObjectInserter; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.lib.ProgressMonitor; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.notes.NoteMap; |
| import org.eclipse.jgit.revwalk.FooterKey; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| |
| import java.io.IOException; |
| import java.util.List; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * This class create code review notes for given {@link CodeReviewCommit}s. |
| * <p> |
| * After the {@link #create(List, PersonIdent)} method is invoked once this |
| * instance must not be reused. Create a new instance of this class if needed. |
| */ |
| public class CreateCodeReviewNotes { |
| public interface Factory { |
| CreateCodeReviewNotes create(ReviewDb reviewDb, Repository db); |
| } |
| |
| private static final FooterKey CHANGE_ID = new FooterKey("Change-Id"); |
| |
| private final AccountCache accountCache; |
| private final ApprovalTypes approvalTypes; |
| private final String canonicalWebUrl; |
| private final String anonymousCowardName; |
| private final ReviewDb schema; |
| private final Repository db; |
| |
| private PersonIdent author; |
| |
| private RevWalk revWalk; |
| private ObjectInserter inserter; |
| |
| private final NotesBranchUtil.Factory notesBranchUtilFactory; |
| |
| @Inject |
| CreateCodeReviewNotes( |
| @GerritPersonIdent final PersonIdent gerritIdent, |
| final AccountCache accountCache, |
| final ApprovalTypes approvalTypes, |
| final @Nullable @CanonicalWebUrl String canonicalWebUrl, |
| final @AnonymousCowardName String anonymousCowardName, |
| final NotesBranchUtil.Factory notesBranchUtilFactory, |
| final @Assisted ReviewDb reviewDb, |
| final @Assisted Repository db) { |
| this.author = gerritIdent; |
| this.accountCache = accountCache; |
| this.approvalTypes = approvalTypes; |
| this.canonicalWebUrl = canonicalWebUrl; |
| this.anonymousCowardName = anonymousCowardName; |
| this.notesBranchUtilFactory = notesBranchUtilFactory; |
| schema = reviewDb; |
| this.db = db; |
| } |
| |
| public void create(List<CodeReviewCommit> commits, PersonIdent author) |
| throws CodeReviewNoteCreationException { |
| try { |
| revWalk = new RevWalk(db); |
| inserter = db.newObjectInserter(); |
| if (author != null) { |
| this.author = author; |
| } |
| |
| NoteMap notes = NoteMap.newEmptyMap(); |
| StringBuilder message = |
| new StringBuilder("Update notes for submitted changes\n\n"); |
| for (CodeReviewCommit c : commits) { |
| notes.set(c, createNoteContent(c.change, c)); |
| message.append("* ").append(c.getShortMessage()).append("\n"); |
| } |
| |
| NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db); |
| notesBranchUtil.commitAllNotes(notes, REFS_NOTES_REVIEW, author, |
| message.toString()); |
| inserter.flush(); |
| } catch (IOException e) { |
| throw new CodeReviewNoteCreationException(e); |
| } catch (ConcurrentRefUpdateException e) { |
| throw new CodeReviewNoteCreationException(e); |
| } finally { |
| revWalk.release(); |
| inserter.release(); |
| } |
| } |
| |
| public void create(List<Change> changes, PersonIdent author, |
| String commitMessage, ProgressMonitor monitor) throws OrmException, |
| IOException, CodeReviewNoteCreationException { |
| try { |
| revWalk = new RevWalk(db); |
| inserter = db.newObjectInserter(); |
| if (author != null) { |
| this.author = author; |
| } |
| if (monitor == null) { |
| monitor = NullProgressMonitor.INSTANCE; |
| } |
| |
| NoteMap notes = NoteMap.newEmptyMap(); |
| for (Change c : changes) { |
| monitor.update(1); |
| PatchSet ps = schema.patchSets().get(c.currentPatchSetId()); |
| ObjectId commitId = ObjectId.fromString(ps.getRevision().get()); |
| notes.set(commitId, createNoteContent(c, commitId)); |
| } |
| |
| NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db); |
| notesBranchUtil.commitAllNotes(notes, REFS_NOTES_REVIEW, author, |
| commitMessage); |
| inserter.flush(); |
| } catch (ConcurrentRefUpdateException e) { |
| throw new CodeReviewNoteCreationException(e); |
| } finally { |
| revWalk.release(); |
| inserter.release(); |
| } |
| } |
| |
| private ObjectId createNoteContent(Change change, ObjectId commit) |
| throws CodeReviewNoteCreationException, IOException { |
| if (!(commit instanceof RevCommit)) { |
| commit = revWalk.parseCommit(commit); |
| } |
| return createNoteContent(change, (RevCommit) commit); |
| } |
| |
| private ObjectId createNoteContent(Change change, RevCommit commit) |
| throws CodeReviewNoteCreationException, IOException { |
| try { |
| ReviewNoteHeaderFormatter formatter = |
| new ReviewNoteHeaderFormatter(author.getTimeZone(), |
| anonymousCowardName); |
| final List<String> idList = commit.getFooterLines(CHANGE_ID); |
| if (idList.isEmpty()) |
| formatter.appendChangeId(change.getKey()); |
| ResultSet<PatchSetApproval> approvals = |
| schema.patchSetApprovals().byPatchSet(change.currentPatchSetId()); |
| PatchSetApproval submit = null; |
| for (PatchSetApproval a : approvals) { |
| if (a.getValue() == 0) { |
| // Ignore 0 values. |
| } else if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) { |
| submit = a; |
| } else { |
| ApprovalType type = approvalTypes.byId(a.getCategoryId()); |
| if (type != null) { |
| formatter.appendApproval( |
| type.getCategory(), |
| a.getValue(), |
| accountCache.get(a.getAccountId()).getAccount()); |
| } |
| } |
| } |
| |
| if (submit != null) { |
| formatter.appendSubmittedBy(accountCache.get(submit.getAccountId()).getAccount()); |
| formatter.appendSubmittedAt(submit.getGranted()); |
| } |
| if (canonicalWebUrl != null) { |
| formatter.appendReviewedOn(canonicalWebUrl, change.getId()); |
| } |
| formatter.appendProject(change.getProject().get()); |
| formatter.appendBranch(change.getDest()); |
| return inserter.insert(Constants.OBJ_BLOB, formatter.toString().getBytes("UTF-8")); |
| } catch (OrmException e) { |
| throw new CodeReviewNoteCreationException(commit, e); |
| } |
| } |
| } |