LCOV - code coverage report
Current view: top level - server/notedb - ChangeNoteUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 72 80 90.0 %
Date: 2022-11-19 15:00:39 Functions: 17 17 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2013 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 com.google.auto.value.AutoValue;
      18             : import com.google.gerrit.entities.Account;
      19             : import com.google.gerrit.entities.AttentionSetUpdate;
      20             : import com.google.gerrit.json.OutputFormat;
      21             : import com.google.gerrit.server.config.GerritServerId;
      22             : import com.google.gson.Gson;
      23             : import com.google.inject.Inject;
      24             : import java.time.Instant;
      25             : import java.util.Optional;
      26             : import org.eclipse.jgit.lib.PersonIdent;
      27             : import org.eclipse.jgit.revwalk.RevCommit;
      28             : import org.eclipse.jgit.util.RawParseUtils;
      29             : 
      30             : public class ChangeNoteUtil {
      31             : 
      32             :   static final String GERRIT_USER_TEMPLATE = "Gerrit User %d";
      33             : 
      34         142 :   private static final Gson gson = OutputFormat.JSON_COMPACT.newGson();
      35             : 
      36             :   private final ChangeNoteJson changeNoteJson;
      37             :   private final String serverId;
      38             : 
      39             :   @Inject
      40         142 :   public ChangeNoteUtil(ChangeNoteJson changeNoteJson, @GerritServerId String serverId) {
      41         142 :     this.serverId = serverId;
      42         142 :     this.changeNoteJson = changeNoteJson;
      43         142 :   }
      44             : 
      45             :   public ChangeNoteJson getChangeNoteJson() {
      46          64 :     return changeNoteJson;
      47             :   }
      48             : 
      49             :   /**
      50             :    * Generates a user identifier that contains the account ID, but not the user's name or email
      51             :    * address.
      52             :    *
      53             :    * @return The passed in {@link StringBuilder} instance to which the identifier has been appended.
      54             :    */
      55             :   StringBuilder appendAccountIdIdentString(StringBuilder stringBuilder, Account.Id accountId) {
      56          75 :     return stringBuilder
      57          75 :         .append(getAccountIdAsUsername(accountId))
      58          75 :         .append(" <")
      59          75 :         .append(getAccountIdAsEmailAddress(accountId))
      60          75 :         .append('>');
      61             :   }
      62             : 
      63             :   public static String formatAccountIdentString(Account.Id account, String accountIdAsEmail) {
      64           1 :     return String.format(
      65           1 :         "%s <%s>", ChangeNoteUtil.getAccountIdAsUsername(account), accountIdAsEmail);
      66             :   }
      67             : 
      68             :   /**
      69             :    * Returns a {@link PersonIdent} that contains the account ID, but not the user's name or email
      70             :    * address.
      71             :    */
      72             :   public PersonIdent newAccountIdIdent(
      73             :       Account.Id accountId, Instant when, PersonIdent serverIdent) {
      74         103 :     return new PersonIdent(
      75         103 :         getAccountIdAsUsername(accountId),
      76         103 :         getAccountIdAsEmailAddress(accountId),
      77             :         when,
      78         103 :         serverIdent.getZoneId());
      79             :   }
      80             : 
      81             :   /** Returns the string {@code "Gerrit User " + accountId}, to pseudonymize user names. */
      82             :   public static String getAccountIdAsUsername(Account.Id accountId) {
      83         103 :     return String.format(GERRIT_USER_TEMPLATE, accountId.get());
      84             :   }
      85             : 
      86             :   public String getAccountIdAsEmailAddress(Account.Id accountId) {
      87         103 :     return accountId.get() + "@" + serverId;
      88             :   }
      89             : 
      90             :   public static Optional<CommitMessageRange> parseCommitMessageRange(RevCommit commit) {
      91         103 :     byte[] raw = commit.getRawBuffer();
      92         103 :     int size = raw.length;
      93             : 
      94         103 :     int subjectStart = RawParseUtils.commitMessage(raw, 0);
      95         103 :     if (subjectStart < 0 || subjectStart >= size) {
      96           0 :       return Optional.empty();
      97             :     }
      98             : 
      99         103 :     int subjectEnd = RawParseUtils.endOfParagraph(raw, subjectStart);
     100         103 :     if (subjectEnd == size) {
     101           1 :       return Optional.empty();
     102             :     }
     103             : 
     104             :     int changeMessageStart;
     105             : 
     106         103 :     if (raw[subjectEnd] == '\n') {
     107         103 :       changeMessageStart = subjectEnd + 2; // \n\n ends paragraph
     108           0 :     } else if (raw[subjectEnd] == '\r') {
     109           0 :       changeMessageStart = subjectEnd + 4; // \r\n\r\n ends paragraph
     110             :     } else {
     111           0 :       return Optional.empty();
     112             :     }
     113             : 
     114         103 :     int ptr = size - 1;
     115         103 :     int changeMessageEnd = -1;
     116         103 :     while (ptr > changeMessageStart) {
     117         103 :       ptr = RawParseUtils.prevLF(raw, ptr, '\r');
     118         103 :       if (ptr == -1) {
     119           0 :         break;
     120             :       }
     121         103 :       if (raw[ptr] == '\n') {
     122         103 :         changeMessageEnd = ptr - 1;
     123         103 :         break;
     124         103 :       } else if (raw[ptr] == '\r') {
     125           0 :         changeMessageEnd = ptr - 3;
     126           0 :         break;
     127             :       }
     128             :     }
     129             : 
     130         103 :     if (ptr <= changeMessageStart) {
     131             :       // Return with subject, ChangeMessage is empty
     132          44 :       return Optional.of(
     133          44 :           CommitMessageRange.builder()
     134          44 :               .subjectStart(subjectStart)
     135          44 :               .subjectEnd(subjectEnd)
     136          44 :               .changeMessageStart(changeMessageStart)
     137          44 :               .changeMessageEnd(changeMessageStart)
     138          44 :               .build());
     139             :     }
     140             : 
     141             :     CommitMessageRange range =
     142         103 :         CommitMessageRange.builder()
     143         103 :             .subjectStart(subjectStart)
     144         103 :             .subjectEnd(subjectEnd)
     145         103 :             .changeMessageStart(changeMessageStart)
     146         103 :             .changeMessageEnd(changeMessageEnd)
     147         103 :             .build();
     148             : 
     149         103 :     return Optional.of(range);
     150             :   }
     151             : 
     152             :   @AutoValue
     153         103 :   public abstract static class CommitMessageRange {
     154             : 
     155             :     public abstract int subjectStart();
     156             : 
     157             :     public abstract int subjectEnd();
     158             : 
     159             :     public abstract int changeMessageStart();
     160             : 
     161             :     public abstract int changeMessageEnd();
     162             : 
     163             :     public boolean hasChangeMessage() {
     164         103 :       return changeMessageStart() < changeMessageEnd();
     165             :     }
     166             : 
     167             :     public static Builder builder() {
     168         103 :       return new AutoValue_ChangeNoteUtil_CommitMessageRange.Builder();
     169             :     }
     170             : 
     171             :     @AutoValue.Builder
     172         103 :     public abstract static class Builder {
     173             : 
     174             :       abstract Builder subjectStart(int subjectStart);
     175             : 
     176             :       abstract Builder subjectEnd(int subjectEnd);
     177             : 
     178             :       abstract Builder changeMessageStart(int changeMessageStart);
     179             : 
     180             :       abstract Builder changeMessageEnd(int changeMessageEnd);
     181             : 
     182             :       abstract CommitMessageRange build();
     183             :     }
     184             :   }
     185             : 
     186             :   /** Helper class for JSON serialization. Timestamp is taken from the commit. */
     187             :   public static class AttentionStatusInNoteDb {
     188             : 
     189             :     final String personIdent;
     190             :     final AttentionSetUpdate.Operation operation;
     191             :     final String reason;
     192             : 
     193             :     AttentionStatusInNoteDb(
     194          52 :         String personIndent, AttentionSetUpdate.Operation operation, String reason) {
     195          52 :       this.personIdent = personIndent;
     196          52 :       this.operation = operation;
     197          52 :       this.reason = reason;
     198          52 :     }
     199             :   }
     200             : 
     201             :   /** The returned {@link Optional} holds the parsed entity or is empty if parsing failed. */
     202             :   static Optional<AttentionSetUpdate> attentionStatusFromJson(
     203             :       Instant timestamp, String attentionString) {
     204          52 :     AttentionStatusInNoteDb inNoteDb =
     205          52 :         gson.fromJson(attentionString, AttentionStatusInNoteDb.class);
     206          52 :     PersonIdent personIdent = RawParseUtils.parsePersonIdent(inNoteDb.personIdent);
     207          52 :     if (personIdent == null) {
     208           0 :       return Optional.empty();
     209             :     }
     210          52 :     Optional<Account.Id> account = NoteDbUtil.parseIdent(personIdent);
     211          52 :     return account.map(
     212             :         id ->
     213          52 :             AttentionSetUpdate.createFromRead(timestamp, id, inNoteDb.operation, inNoteDb.reason));
     214             :   }
     215             : 
     216             :   String attentionSetUpdateToJson(AttentionSetUpdate attentionSetUpdate) {
     217          52 :     StringBuilder stringBuilder = new StringBuilder();
     218          52 :     appendAccountIdIdentString(stringBuilder, attentionSetUpdate.account());
     219          52 :     return gson.toJson(
     220             :         new AttentionStatusInNoteDb(
     221          52 :             stringBuilder.toString(), attentionSetUpdate.operation(), attentionSetUpdate.reason()));
     222             :   }
     223             : }

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