LCOV - code coverage report
Current view: top level - server/notedb - RobotCommentUpdate.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 47 59 79.7 %
Date: 2022-11-19 15:00:39 Functions: 8 9 88.9 %

          Line data    Source code
       1             : // Copyright (C) 2016 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.notedb;
      16             : 
      17             : import static com.google.common.base.MoreObjects.firstNonNull;
      18             : import static com.google.gerrit.entities.RefNames.robotCommentsRef;
      19             : import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
      20             : 
      21             : import com.google.common.collect.Sets;
      22             : import com.google.gerrit.common.Nullable;
      23             : import com.google.gerrit.entities.Account;
      24             : import com.google.gerrit.entities.Change;
      25             : import com.google.gerrit.entities.Project;
      26             : import com.google.gerrit.entities.RobotComment;
      27             : import com.google.gerrit.exceptions.StorageException;
      28             : import com.google.gerrit.server.GerritPersonIdent;
      29             : import com.google.inject.assistedinject.Assisted;
      30             : import com.google.inject.assistedinject.AssistedInject;
      31             : import java.io.IOException;
      32             : import java.time.Instant;
      33             : import java.util.ArrayList;
      34             : import java.util.Arrays;
      35             : import java.util.List;
      36             : import java.util.Map;
      37             : import java.util.Set;
      38             : import org.eclipse.jgit.errors.ConfigInvalidException;
      39             : import org.eclipse.jgit.lib.CommitBuilder;
      40             : import org.eclipse.jgit.lib.ObjectId;
      41             : import org.eclipse.jgit.lib.ObjectInserter;
      42             : import org.eclipse.jgit.lib.PersonIdent;
      43             : import org.eclipse.jgit.notes.NoteMap;
      44             : import org.eclipse.jgit.revwalk.RevWalk;
      45             : 
      46             : /**
      47             :  * A single delta to apply atomically to a change.
      48             :  *
      49             :  * <p>This delta contains only robot comments on a single patch set of a change by a single author.
      50             :  * This delta will become a single commit in the repository.
      51             :  *
      52             :  * <p>This class is not thread safe.
      53             :  */
      54             : public class RobotCommentUpdate extends AbstractChangeUpdate {
      55             :   public interface Factory {
      56             :     RobotCommentUpdate create(
      57             :         ChangeNotes notes,
      58             :         @Assisted("effective") Account.Id accountId,
      59             :         @Assisted("real") Account.Id realAccountId,
      60             :         PersonIdent authorIdent,
      61             :         Instant when);
      62             : 
      63             :     RobotCommentUpdate create(
      64             :         Change change,
      65             :         @Assisted("effective") Account.Id accountId,
      66             :         @Assisted("real") Account.Id realAccountId,
      67             :         PersonIdent authorIdent,
      68             :         Instant when);
      69             :   }
      70             : 
      71           9 :   private List<RobotComment> put = new ArrayList<>();
      72             : 
      73             :   @SuppressWarnings("UnusedMethod")
      74             :   @AssistedInject
      75             :   private RobotCommentUpdate(
      76             :       @GerritPersonIdent PersonIdent serverIdent,
      77             :       ChangeNoteUtil noteUtil,
      78             :       @Assisted ChangeNotes notes,
      79             :       @Assisted("effective") Account.Id accountId,
      80             :       @Assisted("real") Account.Id realAccountId,
      81             :       @Assisted PersonIdent authorIdent,
      82             :       @Assisted Instant when) {
      83           9 :     super(noteUtil, serverIdent, notes, null, accountId, realAccountId, authorIdent, when);
      84           9 :   }
      85             : 
      86             :   @SuppressWarnings("UnusedMethod")
      87             :   @AssistedInject
      88             :   private RobotCommentUpdate(
      89             :       @GerritPersonIdent PersonIdent serverIdent,
      90             :       ChangeNoteUtil noteUtil,
      91             :       @Assisted Change change,
      92             :       @Assisted("effective") Account.Id accountId,
      93             :       @Assisted("real") Account.Id realAccountId,
      94             :       @Assisted PersonIdent authorIdent,
      95             :       @Assisted Instant when) {
      96           0 :     super(noteUtil, serverIdent, null, change, accountId, realAccountId, authorIdent, when);
      97           0 :   }
      98             : 
      99             :   public void putComment(RobotComment c) {
     100           9 :     verifyComment(c);
     101           9 :     put.add(c);
     102           9 :   }
     103             : 
     104             :   @Nullable
     105             :   private CommitBuilder storeCommentsInNotes(
     106             :       RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
     107             :       throws ConfigInvalidException, IOException {
     108           9 :     RevisionNoteMap<RobotCommentsRevisionNote> rnm = getRevisionNoteMap(rw, curr);
     109           9 :     Set<ObjectId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
     110           9 :     RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
     111             : 
     112           9 :     for (RobotComment c : put) {
     113           9 :       cache.get(c.getCommitId()).putComment(c);
     114           9 :     }
     115             : 
     116           9 :     Map<ObjectId, RevisionNoteBuilder> builders = cache.getBuilders();
     117           9 :     boolean touchedAnyRevs = false;
     118           9 :     boolean hasComments = false;
     119           9 :     for (Map.Entry<ObjectId, RevisionNoteBuilder> e : builders.entrySet()) {
     120           9 :       ObjectId id = e.getKey();
     121           9 :       updatedRevs.add(id);
     122           9 :       byte[] data = e.getValue().build(noteUtil);
     123           9 :       if (!Arrays.equals(data, e.getValue().baseRaw)) {
     124           9 :         touchedAnyRevs = true;
     125             :       }
     126           9 :       if (data.length == 0) {
     127           0 :         rnm.noteMap.remove(id);
     128             :       } else {
     129           9 :         hasComments = true;
     130           9 :         ObjectId dataBlob = ins.insert(OBJ_BLOB, data);
     131           9 :         rnm.noteMap.set(id, dataBlob);
     132             :       }
     133           9 :     }
     134             : 
     135             :     // If we didn't touch any notes, tell the caller this was a no-op update. We
     136             :     // couldn't have done this in isEmpty() below because we hadn't read the old
     137             :     // data yet.
     138           9 :     if (!touchedAnyRevs) {
     139           0 :       return NO_OP_UPDATE;
     140             :     }
     141             : 
     142             :     // If we touched every revision and there are no comments left, tell the
     143             :     // caller to delete the entire ref.
     144           9 :     boolean touchedAllRevs = updatedRevs.equals(rnm.revisionNotes.keySet());
     145           9 :     if (touchedAllRevs && !hasComments) {
     146           0 :       return null;
     147             :     }
     148             : 
     149           9 :     cb.setTreeId(rnm.noteMap.writeTree(ins));
     150           9 :     return cb;
     151             :   }
     152             : 
     153             :   private RevisionNoteMap<RobotCommentsRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
     154             :       throws ConfigInvalidException, IOException {
     155           9 :     if (curr.equals(ObjectId.zeroId())) {
     156           9 :       return RevisionNoteMap.emptyMap();
     157             :     }
     158             :     // The old RobotCommentNotes already parsed the revision notes. We can reuse them as long as
     159             :     // the ref hasn't advanced.
     160           3 :     ChangeNotes changeNotes = getNotes();
     161           3 :     if (changeNotes != null) {
     162           3 :       RobotCommentNotes robotCommentNotes = changeNotes.load().getRobotCommentNotes();
     163           3 :       if (robotCommentNotes != null) {
     164           3 :         ObjectId idFromNotes = firstNonNull(robotCommentNotes.getRevision(), ObjectId.zeroId());
     165           3 :         RevisionNoteMap<RobotCommentsRevisionNote> rnm = robotCommentNotes.getRevisionNoteMap();
     166           3 :         if (idFromNotes.equals(curr) && rnm != null) {
     167           3 :           return rnm;
     168             :         }
     169             :       }
     170             :     }
     171             :     NoteMap noteMap;
     172           0 :     if (!curr.equals(ObjectId.zeroId())) {
     173           0 :       noteMap = NoteMap.read(rw.getObjectReader(), rw.parseCommit(curr));
     174             :     } else {
     175           0 :       noteMap = NoteMap.newEmptyMap();
     176             :     }
     177             :     // Even though reading from changes might not be enabled, we need to
     178             :     // parse any existing revision notes so we can merge them.
     179           0 :     return RevisionNoteMap.parseRobotComments(
     180           0 :         noteUtil.getChangeNoteJson(), rw.getObjectReader(), noteMap);
     181             :   }
     182             : 
     183             :   @Override
     184             :   protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
     185             :       throws IOException {
     186           9 :     CommitBuilder cb = new CommitBuilder();
     187           9 :     cb.setMessage("Update robot comments");
     188             :     try {
     189           9 :       return storeCommentsInNotes(rw, ins, curr, cb);
     190           0 :     } catch (ConfigInvalidException e) {
     191           0 :       throw new StorageException(e);
     192             :     }
     193             :   }
     194             : 
     195             :   @Override
     196             :   protected Project.NameKey getProjectName() {
     197           9 :     return getNotes().getProjectName();
     198             :   }
     199             : 
     200             :   @Override
     201             :   protected String getRefName() {
     202           9 :     return robotCommentsRef(getId());
     203             :   }
     204             : 
     205             :   @Override
     206             :   public boolean isEmpty() {
     207           9 :     return put.isEmpty();
     208             :   }
     209             : }

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