Line data Source code
1 : // Copyright (C) 2020 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server; 16 : 17 : import com.google.common.base.Strings; 18 : import com.google.common.collect.ImmutableList; 19 : import com.google.common.collect.ImmutableMap; 20 : import com.google.gerrit.entities.HumanComment; 21 : import com.google.gerrit.entities.PatchSet; 22 : import com.google.gerrit.entities.Project; 23 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 24 : import com.google.gerrit.extensions.restapi.UnprocessableEntityException; 25 : import com.google.gerrit.server.change.EmailReviewComments; 26 : import com.google.gerrit.server.change.NotifyResolver; 27 : import com.google.gerrit.server.extensions.events.CommentAdded; 28 : import com.google.gerrit.server.notedb.ChangeNotes; 29 : import com.google.gerrit.server.notedb.ChangeUpdate; 30 : import com.google.gerrit.server.patch.PatchListNotAvailableException; 31 : import com.google.gerrit.server.update.BatchUpdateOp; 32 : import com.google.gerrit.server.update.ChangeContext; 33 : import com.google.gerrit.server.update.CommentsRejectedException; 34 : import com.google.gerrit.server.update.PostUpdateContext; 35 : import com.google.inject.Inject; 36 : import com.google.inject.assistedinject.Assisted; 37 : import java.io.IOException; 38 : import java.util.ArrayList; 39 : import java.util.List; 40 : import org.eclipse.jgit.lib.ObjectId; 41 : 42 : /** 43 : * A {@link BatchUpdateOp} that can be used to publish draft comments 44 : * 45 : * <p>This class uses the {@link PublishCommentUtil} to publish draft comments and fires the 46 : * necessary event for this. 47 : */ 48 : public class PublishCommentsOp implements BatchUpdateOp { 49 : private final PatchSetUtil psUtil; 50 : private final ChangeNotes.Factory changeNotesFactory; 51 : private final CommentAdded commentAdded; 52 : private final CommentsUtil commentsUtil; 53 : private final EmailReviewComments.Factory email; 54 : private final Project.NameKey projectNameKey; 55 : private final PatchSet.Id psId; 56 : private final PublishCommentUtil publishCommentUtil; 57 : private final ChangeMessagesUtil changeMessagesUtil; 58 : 59 : private ObjectId preUpdateMetaId; 60 4 : private List<HumanComment> comments = new ArrayList<>(); 61 : private String mailMessage; 62 : 63 : public interface Factory { 64 : PublishCommentsOp create(PatchSet.Id psId, Project.NameKey projectNameKey); 65 : } 66 : 67 : @Inject 68 : public PublishCommentsOp( 69 : ChangeNotes.Factory changeNotesFactory, 70 : CommentAdded commentAdded, 71 : CommentsUtil commentsUtil, 72 : EmailReviewComments.Factory email, 73 : PatchSetUtil psUtil, 74 : PublishCommentUtil publishCommentUtil, 75 : ChangeMessagesUtil changeMessagesUtil, 76 : @Assisted PatchSet.Id psId, 77 4 : @Assisted Project.NameKey projectNameKey) { 78 4 : this.changeNotesFactory = changeNotesFactory; 79 4 : this.commentAdded = commentAdded; 80 4 : this.commentsUtil = commentsUtil; 81 4 : this.email = email; 82 4 : this.psId = psId; 83 4 : this.publishCommentUtil = publishCommentUtil; 84 4 : this.psUtil = psUtil; 85 4 : this.projectNameKey = projectNameKey; 86 4 : this.changeMessagesUtil = changeMessagesUtil; 87 4 : } 88 : 89 : @Override 90 : public boolean updateChange(ChangeContext ctx) 91 : throws ResourceConflictException, UnprocessableEntityException, IOException, 92 : PatchListNotAvailableException, CommentsRejectedException { 93 4 : preUpdateMetaId = ctx.getNotes().getMetaId(); 94 4 : comments = commentsUtil.draftByChangeAuthor(ctx.getNotes(), ctx.getUser().getAccountId()); 95 : 96 : // PublishCommentsOp should update a separate ChangeUpdate Object than the one used by other ops 97 : // For example, with the "publish comments on PS upload" workflow, 98 : // There are 2 ops: ReplaceOp & PublishCommentsOp, where each updates its own ChangeUpdate 99 : // This is required since 100 : // 1. a ChangeUpdate has only 1 change message 101 : // 2. Each ChangeUpdate results in 1 commit in NoteDb 102 : // We do it this way so that the execution results in 2 different commits in NoteDb 103 4 : ChangeUpdate changeUpdate = ctx.getDistinctUpdate(psId); 104 4 : publishCommentUtil.publish(ctx, changeUpdate, comments, /* tag= */ null); 105 4 : return insertMessage(changeUpdate); 106 : } 107 : 108 : @Override 109 : public void postUpdate(PostUpdateContext ctx) { 110 4 : if (Strings.isNullOrEmpty(mailMessage) || comments.isEmpty()) { 111 1 : return; 112 : } 113 4 : ChangeNotes changeNotes = changeNotesFactory.createChecked(projectNameKey, psId.changeId()); 114 4 : PatchSet ps = psUtil.get(changeNotes, psId); 115 4 : NotifyResolver.Result notify = ctx.getNotify(changeNotes.getChangeId()); 116 4 : if (notify.shouldNotify()) { 117 4 : email 118 4 : .create( 119 : ctx, 120 : ps, 121 : preUpdateMetaId, 122 : mailMessage, 123 : comments, 124 : /* patchSetComment= */ null, 125 4 : /* labels= */ ImmutableList.of()) 126 4 : .sendAsync(); 127 : } 128 4 : commentAdded.fire( 129 4 : ctx.getChangeData(changeNotes), 130 : ps, 131 4 : ctx.getAccount(), 132 : mailMessage, 133 4 : ImmutableMap.of(), 134 4 : ImmutableMap.of(), 135 4 : ctx.getWhen()); 136 4 : } 137 : 138 : private boolean insertMessage(ChangeUpdate changeUpdate) { 139 4 : StringBuilder buf = new StringBuilder(); 140 4 : if (comments.size() == 1) { 141 4 : buf.append("\n\n(1 comment)"); 142 4 : } else if (comments.size() > 1) { 143 4 : buf.append(String.format("\n\n(%d comments)", comments.size())); 144 : } 145 4 : if (buf.length() == 0) { 146 1 : return false; 147 : } 148 4 : mailMessage = 149 4 : changeMessagesUtil.setChangeMessage( 150 4 : changeUpdate, "Patch Set " + psId.get() + ":" + buf, /* tag= */ null); 151 4 : return true; 152 : } 153 : }