LCOV - code coverage report
Current view: top level - server/restapi/change - PutDraftComment.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 60 64 93.8 %
Date: 2022-11-19 15:00:39 Functions: 5 5 100.0 %

          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.restapi.change;
      16             : 
      17             : import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL;
      18             : 
      19             : import com.google.gerrit.entities.Comment;
      20             : import com.google.gerrit.entities.HumanComment;
      21             : import com.google.gerrit.entities.PatchSet;
      22             : import com.google.gerrit.extensions.api.changes.DraftInput;
      23             : import com.google.gerrit.extensions.common.CommentInfo;
      24             : import com.google.gerrit.extensions.restapi.BadRequestException;
      25             : import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
      26             : import com.google.gerrit.extensions.restapi.Response;
      27             : import com.google.gerrit.extensions.restapi.RestApiException;
      28             : import com.google.gerrit.extensions.restapi.RestModifyView;
      29             : import com.google.gerrit.extensions.restapi.Url;
      30             : import com.google.gerrit.server.CommentsUtil;
      31             : import com.google.gerrit.server.PatchSetUtil;
      32             : import com.google.gerrit.server.change.DraftCommentResource;
      33             : import com.google.gerrit.server.notedb.ChangeUpdate;
      34             : import com.google.gerrit.server.permissions.PermissionBackendException;
      35             : import com.google.gerrit.server.update.BatchUpdate;
      36             : import com.google.gerrit.server.update.BatchUpdateOp;
      37             : import com.google.gerrit.server.update.ChangeContext;
      38             : import com.google.gerrit.server.update.UpdateException;
      39             : import com.google.gerrit.server.util.time.TimeUtil;
      40             : import com.google.inject.Inject;
      41             : import com.google.inject.Provider;
      42             : import com.google.inject.Singleton;
      43             : import java.time.Instant;
      44             : import java.util.Collections;
      45             : import java.util.Optional;
      46             : 
      47             : @Singleton
      48             : public class PutDraftComment implements RestModifyView<DraftCommentResource, DraftInput> {
      49             :   private final BatchUpdate.Factory updateFactory;
      50             :   private final DeleteDraftComment delete;
      51             :   private final CommentsUtil commentsUtil;
      52             :   private final PatchSetUtil psUtil;
      53             :   private final Provider<CommentJson> commentJson;
      54             : 
      55             :   @Inject
      56             :   PutDraftComment(
      57             :       BatchUpdate.Factory updateFactory,
      58             :       DeleteDraftComment delete,
      59             :       CommentsUtil commentsUtil,
      60             :       PatchSetUtil psUtil,
      61         142 :       Provider<CommentJson> commentJson) {
      62         142 :     this.updateFactory = updateFactory;
      63         142 :     this.delete = delete;
      64         142 :     this.commentsUtil = commentsUtil;
      65         142 :     this.psUtil = psUtil;
      66         142 :     this.commentJson = commentJson;
      67         142 :   }
      68             : 
      69             :   @Override
      70             :   public Response<CommentInfo> apply(DraftCommentResource rsrc, DraftInput in)
      71             :       throws RestApiException, UpdateException, PermissionBackendException {
      72           3 :     if (in == null || in.message == null || in.message.trim().isEmpty()) {
      73           1 :       return delete.apply(rsrc, null);
      74           2 :     } else if (in.id != null && !rsrc.getId().equals(in.id)) {
      75           1 :       throw new BadRequestException("id must match URL");
      76           2 :     } else if (in.line != null && in.line < 0) {
      77           0 :       throw new BadRequestException("line must be >= 0");
      78           2 :     } else if (in.path.equals(PATCHSET_LEVEL)
      79             :         && (in.side != null || in.range != null || in.line != null)) {
      80           1 :       throw new BadRequestException("patchset-level comments can't have side, range, or line");
      81           2 :     } else if (in.line != null && in.range != null && in.line != in.range.endLine) {
      82           0 :       throw new BadRequestException("range endLine must be on the same line as the comment");
      83           2 :     } else if (in.inReplyTo != null
      84           1 :         && !commentsUtil.getPublishedHumanComment(rsrc.getNotes(), in.inReplyTo).isPresent()
      85           1 :         && !commentsUtil.getRobotComment(rsrc.getNotes(), in.inReplyTo).isPresent()) {
      86           1 :       throw new BadRequestException(
      87           1 :           String.format("Invalid inReplyTo, comment %s not found", in.inReplyTo));
      88             :     }
      89           2 :     try (BatchUpdate bu =
      90           2 :         updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
      91           2 :       Op op = new Op(rsrc.getComment().key, in);
      92           2 :       bu.addOp(rsrc.getChange().getId(), op);
      93           2 :       bu.execute();
      94           2 :       return Response.ok(
      95           2 :           commentJson.get().setFillAccounts(false).newHumanCommentFormatter().format(op.comment));
      96             :     }
      97             :   }
      98             : 
      99             :   private class Op implements BatchUpdateOp {
     100             :     private final Comment.Key key;
     101             :     private final DraftInput in;
     102             : 
     103             :     private HumanComment comment;
     104             : 
     105           2 :     private Op(Comment.Key key, DraftInput in) {
     106           2 :       this.key = key;
     107           2 :       this.in = in;
     108           2 :     }
     109             : 
     110             :     @Override
     111             :     public boolean updateChange(ChangeContext ctx) throws ResourceNotFoundException {
     112           2 :       Optional<HumanComment> maybeComment =
     113           2 :           commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
     114           2 :       if (!maybeComment.isPresent()) {
     115             :         // Disappeared out from under us. Can't easily fall back to insert,
     116             :         // because the input might be missing required fields. Just give up.
     117           0 :         throw new ResourceNotFoundException("comment not found: " + key);
     118             :       }
     119           2 :       HumanComment origComment = maybeComment.get();
     120           2 :       comment = new HumanComment(origComment);
     121             :       // Copy constructor preserved old real author; replace with current real
     122             :       // user.
     123           2 :       ctx.getUser().updateRealAccountId(comment::setRealAuthor);
     124             : 
     125           2 :       PatchSet.Id psId = PatchSet.id(ctx.getChange().getId(), origComment.key.patchSetId);
     126           2 :       ChangeUpdate update = ctx.getUpdate(psId);
     127             : 
     128           2 :       PatchSet ps = psUtil.get(ctx.getNotes(), psId);
     129           2 :       if (ps == null) {
     130           0 :         throw new ResourceNotFoundException("patch set not found: " + psId);
     131             :       }
     132           2 :       if (in.path != null && !in.path.equals(origComment.key.filename)) {
     133             :         // Updating the path alters the primary key, which isn't possible.
     134             :         // Delete then recreate the comment instead of an update.
     135             : 
     136           1 :         commentsUtil.deleteHumanComments(update, Collections.singleton(origComment));
     137           1 :         comment.key.filename = in.path;
     138             :       }
     139           2 :       commentsUtil.setCommentCommitId(comment, ctx.getChange(), ps);
     140           2 :       commentsUtil.putHumanComments(
     141             :           update,
     142             :           HumanComment.Status.DRAFT,
     143           2 :           Collections.singleton(update(comment, in, ctx.getWhen())));
     144           2 :       return true;
     145             :     }
     146             :   }
     147             : 
     148             :   private static HumanComment update(HumanComment e, DraftInput in, Instant when) {
     149           2 :     if (in.side != null) {
     150           1 :       e.side = in.side();
     151             :     }
     152           2 :     if (in.inReplyTo != null) {
     153           1 :       e.parentUuid = Url.decode(in.inReplyTo);
     154             :     }
     155           2 :     e.setLineNbrAndRange(in.line, in.range);
     156           2 :     e.message = in.message.trim();
     157           2 :     e.setWrittenOn(when);
     158           2 :     if (in.tag != null) {
     159             :       // TODO(dborowitz): Can we support changing tags via PUT?
     160           1 :       e.tag = in.tag;
     161             :     }
     162           2 :     if (in.unresolved != null) {
     163           1 :       e.unresolved = in.unresolved;
     164             :     }
     165           2 :     return e;
     166             :   }
     167             : }

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