LCOV - code coverage report
Current view: top level - server/change - EmailReviewComments.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 54 59 91.5 %
Date: 2022-11-19 15:00:39 Functions: 6 7 85.7 %

          Line data    Source code
       1             : // Copyright (C) 2012 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.change;
      16             : 
      17             : import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
      18             : 
      19             : import com.google.common.collect.ImmutableList;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.common.Nullable;
      22             : import com.google.gerrit.entities.Change;
      23             : import com.google.gerrit.entities.Comment;
      24             : import com.google.gerrit.entities.PatchSet;
      25             : import com.google.gerrit.entities.Project;
      26             : import com.google.gerrit.entities.SubmitRequirement;
      27             : import com.google.gerrit.entities.SubmitRequirementResult;
      28             : import com.google.gerrit.server.CurrentUser;
      29             : import com.google.gerrit.server.IdentifiedUser;
      30             : import com.google.gerrit.server.config.SendEmailExecutor;
      31             : import com.google.gerrit.server.mail.send.CommentSender;
      32             : import com.google.gerrit.server.mail.send.MessageIdGenerator;
      33             : import com.google.gerrit.server.mail.send.MessageIdGenerator.MessageId;
      34             : import com.google.gerrit.server.patch.PatchSetInfoFactory;
      35             : import com.google.gerrit.server.update.PostUpdateContext;
      36             : import com.google.gerrit.server.util.LabelVote;
      37             : import com.google.gerrit.server.util.RequestContext;
      38             : import com.google.gerrit.server.util.ThreadLocalRequestContext;
      39             : import com.google.inject.Inject;
      40             : import com.google.inject.assistedinject.Assisted;
      41             : import java.io.IOException;
      42             : import java.io.UncheckedIOException;
      43             : import java.time.Instant;
      44             : import java.util.List;
      45             : import java.util.Map;
      46             : import java.util.concurrent.ExecutorService;
      47             : import java.util.concurrent.Future;
      48             : import org.eclipse.jgit.lib.ObjectId;
      49             : 
      50             : public class EmailReviewComments {
      51          65 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      52             : 
      53             :   public interface Factory {
      54             :     // TODO(dborowitz/wyatta): Rationalize these arguments so HTML and text templates are operating
      55             :     // on the same set of inputs.
      56             :     /**
      57             :      * Creates handle for sending email
      58             :      *
      59             :      * @param postUpdateContext the post update context from the calling BatchUpdateOp
      60             :      * @param patchSet patch set corresponding to the top-level op
      61             :      * @param preUpdateMetaId the SHA1 to which the notes branch pointed before the update
      62             :      * @param message used by text template only. The contents of this message typically include the
      63             :      *     "Patch set N" header and "(M comments)".
      64             :      * @param comments inline comments.
      65             :      * @param patchSetComment used by HTML template only: some quasi-human-generated text. The
      66             :      *     contents should *not* include a "Patch set N" header or "(M comments)" footer, as these
      67             :      *     will be added automatically in soy in a structured way.
      68             :      * @param labels labels applied as part of this review operation.
      69             :      */
      70             :     EmailReviewComments create(
      71             :         PostUpdateContext postUpdateContext,
      72             :         PatchSet patchSet,
      73             :         ObjectId preUpdateMetaId,
      74             :         @Assisted("message") String message,
      75             :         List<? extends Comment> comments,
      76             :         @Nullable @Assisted("patchSetComment") String patchSetComment,
      77             :         List<LabelVote> labels);
      78             :   }
      79             : 
      80             :   private final ExecutorService sendEmailsExecutor;
      81             :   private final AsyncSender asyncSender;
      82             : 
      83             :   @Inject
      84             :   EmailReviewComments(
      85             :       @SendEmailExecutor ExecutorService executor,
      86             :       PatchSetInfoFactory patchSetInfoFactory,
      87             :       CommentSender.Factory commentSenderFactory,
      88             :       ThreadLocalRequestContext requestContext,
      89             :       MessageIdGenerator messageIdGenerator,
      90             :       @Assisted PostUpdateContext postUpdateContext,
      91             :       @Assisted PatchSet patchSet,
      92             :       @Assisted ObjectId preUpdateMetaId,
      93             :       @Assisted("message") String message,
      94             :       @Assisted List<? extends Comment> comments,
      95             :       @Nullable @Assisted("patchSetComment") String patchSetComment,
      96          65 :       @Assisted List<LabelVote> labels) {
      97          65 :     this.sendEmailsExecutor = executor;
      98             : 
      99             :     MessageId messageId;
     100             :     try {
     101          65 :       messageId =
     102          65 :           messageIdGenerator.fromChangeUpdateAndReason(
     103          65 :               postUpdateContext.getRepoView(), patchSet.id(), "EmailReviewComments");
     104           0 :     } catch (IOException e) {
     105           0 :       throw new UncheckedIOException(e);
     106          65 :     }
     107             : 
     108          65 :     Change.Id changeId = patchSet.id().changeId();
     109             : 
     110             :     // Getting the change data from PostUpdateContext retrieves a cached ChangeData
     111             :     // instance. This ChangeData instance has been created when the change was (re)indexed
     112             :     // due to the update, and hence has submit requirement results already cached (since
     113             :     // (re)indexing triggers the evaluation of the submit requirements).
     114          65 :     Map<SubmitRequirement, SubmitRequirementResult> postUpdateSubmitRequirementResults =
     115             :         postUpdateContext
     116          65 :             .getChangeData(postUpdateContext.getProject(), changeId)
     117          65 :             .submitRequirementsIncludingLegacy();
     118          65 :     this.asyncSender =
     119             :         new AsyncSender(
     120             :             requestContext,
     121             :             commentSenderFactory,
     122             :             patchSetInfoFactory,
     123          65 :             postUpdateContext.getUser().asIdentifiedUser(),
     124             :             messageId,
     125          65 :             postUpdateContext.getNotify(changeId),
     126          65 :             postUpdateContext.getProject(),
     127             :             changeId,
     128             :             patchSet,
     129             :             preUpdateMetaId,
     130             :             message,
     131          65 :             postUpdateContext.getWhen(),
     132          65 :             ImmutableList.copyOf(COMMENT_ORDER.sortedCopy(comments)),
     133             :             patchSetComment,
     134          65 :             ImmutableList.copyOf(labels),
     135             :             postUpdateSubmitRequirementResults);
     136          65 :   }
     137             : 
     138             :   public void sendAsync() {
     139             :     @SuppressWarnings("unused")
     140          65 :     Future<?> possiblyIgnoredError = sendEmailsExecutor.submit(asyncSender);
     141          65 :   }
     142             : 
     143             :   /**
     144             :    * {@link Runnable} that sends the email asynchonously.
     145             :    *
     146             :    * <p>Only pass objects into this class that are thread-safe (e.g. immutable) so that they can be
     147             :    * safely accessed from the background thread.
     148             :    */
     149             :   // TODO: The passed in Comment class is not thread-safe, replace it with an AutoValue type.
     150             :   private static class AsyncSender implements Runnable, RequestContext {
     151             :     private final ThreadLocalRequestContext requestContext;
     152             :     private final CommentSender.Factory commentSenderFactory;
     153             :     private final PatchSetInfoFactory patchSetInfoFactory;
     154             :     private final IdentifiedUser user;
     155             :     private final MessageId messageId;
     156             :     private final NotifyResolver.Result notify;
     157             :     private final Project.NameKey projectName;
     158             :     private final Change.Id changeId;
     159             :     private final PatchSet patchSet;
     160             :     private final ObjectId preUpdateMetaId;
     161             :     private final String message;
     162             :     private final Instant timestamp;
     163             :     private final ImmutableList<? extends Comment> comments;
     164             :     @Nullable private final String patchSetComment;
     165             :     private final ImmutableList<LabelVote> labels;
     166             :     private final Map<SubmitRequirement, SubmitRequirementResult>
     167             :         postUpdateSubmitRequirementResults;
     168             : 
     169             :     AsyncSender(
     170             :         ThreadLocalRequestContext requestContext,
     171             :         CommentSender.Factory commentSenderFactory,
     172             :         PatchSetInfoFactory patchSetInfoFactory,
     173             :         IdentifiedUser user,
     174             :         MessageId messageId,
     175             :         NotifyResolver.Result notify,
     176             :         Project.NameKey projectName,
     177             :         Change.Id changeId,
     178             :         PatchSet patchSet,
     179             :         ObjectId preUpdateMetaId,
     180             :         String message,
     181             :         Instant timestamp,
     182             :         ImmutableList<? extends Comment> comments,
     183             :         @Nullable String patchSetComment,
     184             :         ImmutableList<LabelVote> labels,
     185          65 :         Map<SubmitRequirement, SubmitRequirementResult> postUpdateSubmitRequirementResults) {
     186          65 :       this.requestContext = requestContext;
     187          65 :       this.commentSenderFactory = commentSenderFactory;
     188          65 :       this.patchSetInfoFactory = patchSetInfoFactory;
     189          65 :       this.user = user;
     190          65 :       this.messageId = messageId;
     191          65 :       this.notify = notify;
     192          65 :       this.projectName = projectName;
     193          65 :       this.changeId = changeId;
     194          65 :       this.patchSet = patchSet;
     195          65 :       this.preUpdateMetaId = preUpdateMetaId;
     196          65 :       this.message = message;
     197          65 :       this.timestamp = timestamp;
     198          65 :       this.comments = comments;
     199          65 :       this.patchSetComment = patchSetComment;
     200          65 :       this.labels = labels;
     201          65 :       this.postUpdateSubmitRequirementResults = postUpdateSubmitRequirementResults;
     202          65 :     }
     203             : 
     204             :     @Override
     205             :     public void run() {
     206          65 :       RequestContext old = requestContext.setContext(this);
     207             :       try {
     208          65 :         CommentSender emailSender =
     209          65 :             commentSenderFactory.create(
     210             :                 projectName, changeId, preUpdateMetaId, postUpdateSubmitRequirementResults);
     211          65 :         emailSender.setFrom(user.getAccountId());
     212          65 :         emailSender.setPatchSet(patchSet, patchSetInfoFactory.get(projectName, patchSet));
     213          65 :         emailSender.setChangeMessage(message, timestamp);
     214          65 :         emailSender.setComments(comments);
     215          65 :         emailSender.setPatchSetComment(patchSetComment);
     216          65 :         emailSender.setLabels(labels);
     217          65 :         emailSender.setNotify(notify);
     218          65 :         emailSender.setMessageId(messageId);
     219          65 :         emailSender.send();
     220           0 :       } catch (Exception e) {
     221           0 :         logger.atSevere().withCause(e).log("Cannot email comments for %s", patchSet.id());
     222             :       } finally {
     223          65 :         requestContext.setContext(old);
     224             :       }
     225          65 :     }
     226             : 
     227             :     @Override
     228             :     public String toString() {
     229           0 :       return "send-email comments";
     230             :     }
     231             : 
     232             :     @Override
     233             :     public CurrentUser getUser() {
     234          65 :       return user.getRealUser();
     235             :     }
     236             :   }
     237             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750