LCOV - code coverage report
Current view: top level - server/change - EmailNewPatchSet.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 62 67 92.5 %
Date: 2022-11-19 15:00:39 Functions: 8 9 88.9 %

          Line data    Source code
       1             : // Copyright (C) 2022 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 com.google.common.collect.ImmutableSet;
      18             : import com.google.common.flogger.FluentLogger;
      19             : import com.google.gerrit.entities.Account;
      20             : import com.google.gerrit.entities.Change;
      21             : import com.google.gerrit.entities.PatchSet;
      22             : import com.google.gerrit.entities.PatchSetApproval;
      23             : import com.google.gerrit.entities.Project;
      24             : import com.google.gerrit.entities.SubmitRequirement;
      25             : import com.google.gerrit.entities.SubmitRequirementResult;
      26             : import com.google.gerrit.extensions.client.ChangeKind;
      27             : import com.google.gerrit.server.CurrentUser;
      28             : import com.google.gerrit.server.IdentifiedUser;
      29             : import com.google.gerrit.server.config.SendEmailExecutor;
      30             : import com.google.gerrit.server.mail.send.MessageIdGenerator;
      31             : import com.google.gerrit.server.mail.send.MessageIdGenerator.MessageId;
      32             : import com.google.gerrit.server.mail.send.ReplacePatchSetSender;
      33             : import com.google.gerrit.server.patch.PatchSetInfoFactory;
      34             : import com.google.gerrit.server.update.PostUpdateContext;
      35             : import com.google.gerrit.server.util.RequestContext;
      36             : import com.google.gerrit.server.util.RequestScopePropagator;
      37             : import com.google.gerrit.server.util.ThreadLocalRequestContext;
      38             : import com.google.inject.Inject;
      39             : import com.google.inject.assistedinject.Assisted;
      40             : import java.io.IOException;
      41             : import java.io.UncheckedIOException;
      42             : import java.time.Instant;
      43             : import java.util.Map;
      44             : import java.util.concurrent.ExecutorService;
      45             : import java.util.concurrent.Future;
      46             : import org.eclipse.jgit.lib.ObjectId;
      47             : 
      48             : public class EmailNewPatchSet {
      49          50 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      50             : 
      51             :   public interface Factory {
      52             :     EmailNewPatchSet create(
      53             :         PostUpdateContext postUpdateContext,
      54             :         PatchSet patchSet,
      55             :         String message,
      56             :         ImmutableSet<PatchSetApproval> outdatedApprovals,
      57             :         @Assisted("reviewers") ImmutableSet<Account.Id> reviewers,
      58             :         @Assisted("extraCcs") ImmutableSet<Account.Id> extraCcs,
      59             :         ChangeKind changeKind,
      60             :         ObjectId preUpdateMetaId);
      61             :   }
      62             : 
      63             :   private final ExecutorService sendEmailExecutor;
      64             :   private final ThreadLocalRequestContext threadLocalRequestContext;
      65             :   private final AsyncSender asyncSender;
      66             : 
      67             :   private RequestScopePropagator requestScopePropagator;
      68             : 
      69             :   @Inject
      70             :   EmailNewPatchSet(
      71             :       @SendEmailExecutor ExecutorService sendEmailExecutor,
      72             :       ThreadLocalRequestContext threadLocalRequestContext,
      73             :       ReplacePatchSetSender.Factory replacePatchSetFactory,
      74             :       PatchSetInfoFactory patchSetInfoFactory,
      75             :       MessageIdGenerator messageIdGenerator,
      76             :       @Assisted PostUpdateContext postUpdateContext,
      77             :       @Assisted PatchSet patchSet,
      78             :       @Assisted String message,
      79             :       @Assisted ImmutableSet<PatchSetApproval> outdatedApprovals,
      80             :       @Assisted("reviewers") ImmutableSet<Account.Id> reviewers,
      81             :       @Assisted("extraCcs") ImmutableSet<Account.Id> extraCcs,
      82             :       @Assisted ChangeKind changeKind,
      83          50 :       @Assisted ObjectId preUpdateMetaId) {
      84          50 :     this.sendEmailExecutor = sendEmailExecutor;
      85          50 :     this.threadLocalRequestContext = threadLocalRequestContext;
      86             : 
      87             :     MessageId messageId;
      88             :     try {
      89          50 :       messageId =
      90          50 :           messageIdGenerator.fromChangeUpdateAndReason(
      91          50 :               postUpdateContext.getRepoView(), patchSet.id(), "EmailReplacePatchSet");
      92           0 :     } catch (IOException e) {
      93           0 :       throw new UncheckedIOException(e);
      94          50 :     }
      95             : 
      96          50 :     Change.Id changeId = patchSet.id().changeId();
      97             : 
      98             :     // Getting the change data from PostUpdateContext retrieves a cached ChangeData
      99             :     // instance. This ChangeData instance has been created when the change was (re)indexed
     100             :     // due to the update, and hence has submit requirement results already cached (since
     101             :     // (re)indexing triggers the evaluation of the submit requirements).
     102          50 :     Map<SubmitRequirement, SubmitRequirementResult> postUpdateSubmitRequirementResults =
     103             :         postUpdateContext
     104          50 :             .getChangeData(postUpdateContext.getProject(), changeId)
     105          50 :             .submitRequirementsIncludingLegacy();
     106          50 :     this.asyncSender =
     107             :         new AsyncSender(
     108          50 :             postUpdateContext.getIdentifiedUser(),
     109             :             replacePatchSetFactory,
     110             :             patchSetInfoFactory,
     111             :             messageId,
     112          50 :             postUpdateContext.getNotify(changeId),
     113          50 :             postUpdateContext.getProject(),
     114             :             changeId,
     115             :             patchSet,
     116             :             message,
     117          50 :             postUpdateContext.getWhen(),
     118             :             outdatedApprovals,
     119             :             reviewers,
     120             :             extraCcs,
     121             :             changeKind,
     122             :             preUpdateMetaId,
     123             :             postUpdateSubmitRequirementResults);
     124          50 :   }
     125             : 
     126             :   public EmailNewPatchSet setRequestScopePropagator(RequestScopePropagator requestScopePropagator) {
     127          37 :     this.requestScopePropagator = requestScopePropagator;
     128          37 :     return this;
     129             :   }
     130             : 
     131             :   public void sendAsync() {
     132             :     @SuppressWarnings("unused")
     133          50 :     Future<?> possiblyIgnoredError =
     134          50 :         sendEmailExecutor.submit(
     135          50 :             requestScopePropagator != null
     136          37 :                 ? requestScopePropagator.wrap(asyncSender)
     137          28 :                 : () -> {
     138          28 :                   RequestContext old = threadLocalRequestContext.setContext(asyncSender);
     139             :                   try {
     140          28 :                     asyncSender.run();
     141             :                   } finally {
     142          28 :                     threadLocalRequestContext.setContext(old);
     143             :                   }
     144          28 :                 });
     145          50 :   }
     146             : 
     147             :   /**
     148             :    * {@link Runnable} that sends the email asynchonously.
     149             :    *
     150             :    * <p>Only pass objects into this class that are thread-safe (e.g. immutable) so that they can be
     151             :    * safely accessed from the background thread.
     152             :    */
     153             :   private static class AsyncSender implements Runnable, RequestContext {
     154             :     private final IdentifiedUser user;
     155             :     private final ReplacePatchSetSender.Factory replacePatchSetFactory;
     156             :     private final PatchSetInfoFactory patchSetInfoFactory;
     157             :     private final MessageId messageId;
     158             :     private final NotifyResolver.Result notify;
     159             :     private final Project.NameKey projectName;
     160             :     private final Change.Id changeId;
     161             :     private final PatchSet patchSet;
     162             :     private final String message;
     163             :     private final Instant timestamp;
     164             :     private final ImmutableSet<PatchSetApproval> outdatedApprovals;
     165             :     private final ImmutableSet<Account.Id> reviewers;
     166             :     private final ImmutableSet<Account.Id> extraCcs;
     167             :     private final ChangeKind changeKind;
     168             :     private final ObjectId preUpdateMetaId;
     169             :     private final Map<SubmitRequirement, SubmitRequirementResult>
     170             :         postUpdateSubmitRequirementResults;
     171             : 
     172             :     AsyncSender(
     173             :         IdentifiedUser user,
     174             :         ReplacePatchSetSender.Factory replacePatchSetFactory,
     175             :         PatchSetInfoFactory patchSetInfoFactory,
     176             :         MessageId messageId,
     177             :         NotifyResolver.Result notify,
     178             :         Project.NameKey projectName,
     179             :         Change.Id changeId,
     180             :         PatchSet patchSet,
     181             :         String message,
     182             :         Instant timestamp,
     183             :         ImmutableSet<PatchSetApproval> outdatedApprovals,
     184             :         ImmutableSet<Account.Id> reviewers,
     185             :         ImmutableSet<Account.Id> extraCcs,
     186             :         ChangeKind changeKind,
     187             :         ObjectId preUpdateMetaId,
     188          50 :         Map<SubmitRequirement, SubmitRequirementResult> postUpdateSubmitRequirementResults) {
     189          50 :       this.user = user;
     190          50 :       this.replacePatchSetFactory = replacePatchSetFactory;
     191          50 :       this.patchSetInfoFactory = patchSetInfoFactory;
     192          50 :       this.messageId = messageId;
     193          50 :       this.notify = notify;
     194          50 :       this.projectName = projectName;
     195          50 :       this.changeId = changeId;
     196          50 :       this.patchSet = patchSet;
     197          50 :       this.message = message;
     198          50 :       this.timestamp = timestamp;
     199          50 :       this.outdatedApprovals = outdatedApprovals;
     200          50 :       this.reviewers = reviewers;
     201          50 :       this.extraCcs = extraCcs;
     202          50 :       this.changeKind = changeKind;
     203          50 :       this.preUpdateMetaId = preUpdateMetaId;
     204          50 :       this.postUpdateSubmitRequirementResults = postUpdateSubmitRequirementResults;
     205          50 :     }
     206             : 
     207             :     @Override
     208             :     public void run() {
     209             :       try {
     210          50 :         ReplacePatchSetSender emailSender =
     211          50 :             replacePatchSetFactory.create(
     212             :                 projectName,
     213             :                 changeId,
     214             :                 changeKind,
     215             :                 preUpdateMetaId,
     216             :                 postUpdateSubmitRequirementResults);
     217          50 :         emailSender.setFrom(user.getAccountId());
     218          50 :         emailSender.setPatchSet(patchSet, patchSetInfoFactory.get(projectName, patchSet));
     219          50 :         emailSender.setChangeMessage(message, timestamp);
     220          50 :         emailSender.setNotify(notify);
     221          50 :         emailSender.addReviewers(reviewers);
     222          50 :         emailSender.addExtraCC(extraCcs);
     223          50 :         emailSender.addOutdatedApproval(outdatedApprovals);
     224          50 :         emailSender.setMessageId(messageId);
     225          50 :         emailSender.send();
     226           0 :       } catch (Exception e) {
     227           0 :         logger.atSevere().withCause(e).log("Cannot send email for new patch set %s", patchSet.id());
     228          50 :       }
     229          50 :     }
     230             : 
     231             :     @Override
     232             :     public String toString() {
     233           0 :       return "send-email newpatchset";
     234             :     }
     235             : 
     236             :     @Override
     237             :     public CurrentUser getUser() {
     238          28 :       return user.getRealUser();
     239             :     }
     240             :   }
     241             : }

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