LCOV - code coverage report
Current view: top level - server - PublishCommentUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 30 33 90.9 %
Date: 2022-11-19 15:00:39 Functions: 7 7 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2018 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 static com.google.common.base.Preconditions.checkArgument;
      18             : import static java.util.stream.Collectors.toSet;
      19             : 
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.common.flogger.FluentLogger;
      22             : import com.google.gerrit.common.Nullable;
      23             : import com.google.gerrit.entities.HumanComment;
      24             : import com.google.gerrit.entities.PatchSet;
      25             : import com.google.gerrit.extensions.validators.CommentForValidation;
      26             : import com.google.gerrit.extensions.validators.CommentValidationContext;
      27             : import com.google.gerrit.extensions.validators.CommentValidationFailure;
      28             : import com.google.gerrit.extensions.validators.CommentValidator;
      29             : import com.google.gerrit.server.notedb.ChangeNotes;
      30             : import com.google.gerrit.server.notedb.ChangeUpdate;
      31             : import com.google.gerrit.server.plugincontext.PluginSetContext;
      32             : import com.google.gerrit.server.update.ChangeContext;
      33             : import com.google.inject.Inject;
      34             : import com.google.inject.Singleton;
      35             : import java.sql.Timestamp;
      36             : import java.util.Collection;
      37             : import java.util.HashSet;
      38             : import java.util.Map;
      39             : import java.util.Set;
      40             : 
      41             : @Singleton
      42             : public class PublishCommentUtil {
      43         142 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      44             : 
      45             :   private final PatchSetUtil psUtil;
      46             :   private final CommentsUtil commentsUtil;
      47             : 
      48             :   @Inject
      49         142 :   PublishCommentUtil(CommentsUtil commentsUtil, PatchSetUtil psUtil) {
      50         142 :     this.commentsUtil = commentsUtil;
      51         142 :     this.psUtil = psUtil;
      52         142 :   }
      53             : 
      54             :   public void publish(
      55             :       ChangeContext ctx,
      56             :       ChangeUpdate changeUpdate,
      57             :       Collection<HumanComment> draftComments,
      58             :       @Nullable String tag) {
      59          12 :     ChangeNotes notes = ctx.getNotes();
      60          12 :     checkArgument(notes != null);
      61          12 :     if (draftComments.isEmpty()) {
      62           4 :       return;
      63             :     }
      64             : 
      65           9 :     Map<PatchSet.Id, PatchSet> patchSets =
      66           9 :         psUtil.getAsMap(notes, draftComments.stream().map(d -> psId(notes, d)).collect(toSet()));
      67           9 :     Set<HumanComment> commentsToPublish = new HashSet<>();
      68           9 :     for (HumanComment draftComment : draftComments) {
      69           9 :       PatchSet.Id psIdOfDraftComment = psId(notes, draftComment);
      70           9 :       PatchSet ps = patchSets.get(psIdOfDraftComment);
      71           9 :       if (ps == null) {
      72             :         // This can happen if changes with the same numeric ID exist:
      73             :         // - change 12345 has 3 patch sets in repo X
      74             :         // - another change 12345 has 7 patch sets in repo Y
      75             :         // - the user saves a draft comment on patch set 6 of the change in repo Y
      76             :         // - this draft comment gets stored in:
      77             :         //   AllUsers -> refs/draft-comments/45/12345/<account-id>
      78             :         // - when posting a review with draft handling PUBLISH_ALL_REVISIONS on the change in
      79             :         //   repo X, the draft comments are loaded from
      80             :         //   AllUsers -> refs/draft-comments/45/12345/<account-id>, including the draft
      81             :         //   comment that was saved for patch set 6 of the change in repo Y
      82             :         // - patch set 6 does not exist for the change in repo x, hence we get null for the patch
      83             :         //   set here
      84             :         // Instead of failing hard (and returning an Internal Server Error) to the caller,
      85             :         // just ignore that comment.
      86             :         // Gerrit ensures that numeric change IDs are unique, but you can get duplicates if
      87             :         // change refs of one repo are copied/pushed to another repo on the same host (this
      88             :         // should never be done, but we know it happens).
      89           0 :         logger.atWarning().log(
      90             :             "Ignoring draft comment %s on non existing patch set %s (repo = %s)",
      91           0 :             draftComment, psIdOfDraftComment, notes.getProjectName());
      92           0 :         continue;
      93             :       }
      94           9 :       draftComment.writtenOn = Timestamp.from(ctx.getWhen());
      95           9 :       draftComment.tag = tag;
      96             :       // Draft may have been created by a different real user; copy the current real user. (Only
      97             :       // applies to X-Gerrit-RunAs, since modifying drafts via on_behalf_of is not allowed.)
      98           9 :       ctx.getUser().updateRealAccountId(draftComment::setRealAuthor);
      99           9 :       commentsUtil.setCommentCommitId(draftComment, notes.getChange(), ps);
     100           9 :       commentsToPublish.add(draftComment);
     101           9 :     }
     102           9 :     commentsUtil.putHumanComments(changeUpdate, HumanComment.Status.PUBLISHED, commentsToPublish);
     103           9 :   }
     104             : 
     105             :   private static PatchSet.Id psId(ChangeNotes notes, HumanComment c) {
     106           9 :     return PatchSet.id(notes.getChangeId(), c.key.patchSetId);
     107             :   }
     108             : 
     109             :   /**
     110             :    * Helper to run the specified set of {@link CommentValidator}-s on the specified comments.
     111             :    *
     112             :    * @return See {@link CommentValidator#validateComments(CommentValidationContext,ImmutableList)}.
     113             :    */
     114             :   public static ImmutableList<CommentValidationFailure> findInvalidComments(
     115             :       CommentValidationContext ctx,
     116             :       PluginSetContext<CommentValidator> commentValidators,
     117             :       ImmutableList<CommentForValidation> commentsForValidation) {
     118          65 :     ImmutableList.Builder<CommentValidationFailure> commentValidationFailures =
     119             :         new ImmutableList.Builder<>();
     120          65 :     commentValidators.runEach(
     121             :         validator ->
     122          65 :             commentValidationFailures.addAll(
     123          65 :                 validator.validateComments(ctx, commentsForValidation)));
     124          65 :     return commentValidationFailures.build();
     125             :   }
     126             : }

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