LCOV - code coverage report
Current view: top level - server/query/change - EqualsLabelPredicate.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 59 66 89.4 %
Date: 2022-11-19 15:00:39 Functions: 6 6 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.query.change;
      16             : 
      17             : import com.google.gerrit.common.Nullable;
      18             : import com.google.gerrit.entities.Account;
      19             : import com.google.gerrit.entities.AccountGroup;
      20             : import com.google.gerrit.entities.Change;
      21             : import com.google.gerrit.entities.LabelType;
      22             : import com.google.gerrit.entities.LabelTypes;
      23             : import com.google.gerrit.entities.PatchSetApproval;
      24             : import com.google.gerrit.extensions.restapi.AuthException;
      25             : import com.google.gerrit.server.IdentifiedUser;
      26             : import com.google.gerrit.server.index.change.ChangeField;
      27             : import com.google.gerrit.server.permissions.ChangePermission;
      28             : import com.google.gerrit.server.permissions.PermissionBackend;
      29             : import com.google.gerrit.server.permissions.PermissionBackendException;
      30             : import com.google.gerrit.server.project.ProjectCache;
      31             : import com.google.gerrit.server.project.ProjectState;
      32             : import com.google.gerrit.server.query.change.ChangeData.StorageConstraint;
      33             : import java.util.Optional;
      34             : 
      35             : public class EqualsLabelPredicate extends ChangeIndexPostFilterPredicate {
      36             :   protected final ProjectCache projectCache;
      37             :   protected final PermissionBackend permissionBackend;
      38             :   protected final IdentifiedUser.GenericFactory userFactory;
      39             :   /** label name to be matched. */
      40             :   protected final String label;
      41             : 
      42             :   /** Expected vote value for the label. */
      43             :   protected final int expVal;
      44             : 
      45             :   /**
      46             :    * Number of times the value {@link #expVal} for label {@link #label} should occur. If null, match
      47             :    * with any count greater or equal to 1.
      48             :    */
      49             :   @Nullable protected final Integer count;
      50             : 
      51             :   /** Account ID that has voted on the label. */
      52             :   protected final Account.Id account;
      53             : 
      54             :   protected final AccountGroup.UUID group;
      55             : 
      56             :   public EqualsLabelPredicate(
      57             :       LabelPredicate.Args args,
      58             :       String label,
      59             :       int expVal,
      60             :       Account.Id account,
      61             :       @Nullable Integer count) {
      62          13 :     super(ChangeField.LABEL, ChangeField.formatLabel(label, expVal, account, count));
      63          13 :     this.permissionBackend = args.permissionBackend;
      64          13 :     this.projectCache = args.projectCache;
      65          13 :     this.userFactory = args.userFactory;
      66          13 :     this.count = count;
      67          13 :     this.group = args.group;
      68          13 :     this.label = label;
      69          13 :     this.expVal = expVal;
      70          13 :     this.account = account;
      71          13 :   }
      72             : 
      73             :   @Override
      74             :   public boolean match(ChangeData object) {
      75          12 :     Change c = object.change();
      76          12 :     if (c == null) {
      77             :       // The change has disappeared.
      78             :       //
      79           0 :       return false;
      80             :     }
      81             : 
      82          12 :     if (Integer.valueOf(0).equals(count)) {
      83             :       // We don't match against count=0 so that the computation is identical to the stored values
      84             :       // in the index. We do that since computing count=0 requires looping on all {label_type,
      85             :       // vote_value} for the change and storing a {count=0} format for it in the change index which
      86             :       // is computationally expensive.
      87           4 :       return false;
      88             :     }
      89             : 
      90          12 :     Optional<ProjectState> project = projectCache.get(c.getDest().project());
      91          12 :     if (!project.isPresent()) {
      92             :       // The project has disappeared.
      93             :       //
      94           0 :       return false;
      95             :     }
      96             : 
      97          12 :     LabelType labelType = type(project.get().getLabelTypes(), label);
      98          12 :     if (labelType == null) {
      99          11 :       return false; // Label is not defined by this project.
     100             :     }
     101             : 
     102           9 :     boolean hasVote = false;
     103           9 :     int matchingVotes = 0;
     104           9 :     StorageConstraint currentStorageConstraint = object.getStorageConstraint();
     105           9 :     object.setStorageConstraint(ChangeData.StorageConstraint.INDEX_PRIMARY_NOTEDB_SECONDARY);
     106           9 :     for (PatchSetApproval p : object.currentApprovals()) {
     107           8 :       if (labelType.matches(p)) {
     108           8 :         hasVote = true;
     109           8 :         if (match(object, p.value(), p.accountId())) {
     110           8 :           matchingVotes += 1;
     111             :         }
     112             :       }
     113           8 :     }
     114           9 :     object.setStorageConstraint(currentStorageConstraint);
     115           9 :     if (!hasVote && expVal == 0) {
     116           0 :       return true;
     117             :     }
     118             : 
     119           9 :     return count == null ? matchingVotes >= 1 : matchingVotes == count;
     120             :   }
     121             : 
     122             :   @Nullable
     123             :   protected static LabelType type(LabelTypes types, String toFind) {
     124          12 :     if (types.byLabel(toFind).isPresent()) {
     125           9 :       return types.byLabel(toFind).get();
     126             :     }
     127             : 
     128          11 :     for (LabelType lt : types.getLabelTypes()) {
     129          11 :       if (toFind.equalsIgnoreCase(lt.getName())) {
     130           0 :         return lt;
     131             :       }
     132          11 :     }
     133          11 :     return null;
     134             :   }
     135             : 
     136             :   protected boolean match(ChangeData cd, short value, Account.Id approver) {
     137           8 :     if (value != expVal) {
     138           5 :       return false;
     139             :     }
     140             : 
     141           8 :     if (account != null) {
     142             :       // case when account in query is numeric
     143           5 :       if (!account.equals(approver) && !isMagicUser()) {
     144           4 :         return false;
     145             :       }
     146             : 
     147             :       // case when account in query = owner
     148           5 :       if (account.equals(ChangeQueryBuilder.OWNER_ACCOUNT_ID)
     149           4 :           && !cd.change().getOwner().equals(approver)) {
     150           4 :         return false;
     151             :       }
     152             : 
     153             :       // case when account in query = non_uploader
     154           5 :       if (account.equals(ChangeQueryBuilder.NON_UPLOADER_ACCOUNT_ID)
     155           5 :           && cd.currentPatchSet().uploader().equals(approver)) {
     156           5 :         return false;
     157             :       }
     158             :     }
     159             : 
     160           8 :     IdentifiedUser reviewer = userFactory.create(approver);
     161           8 :     if (group != null && !reviewer.getEffectiveGroups().contains(group)) {
     162           4 :       return false;
     163             :     }
     164             : 
     165             :     // Check the user has 'READ' permission.
     166             :     try {
     167           8 :       PermissionBackend.ForChange perm = permissionBackend.absentUser(approver).change(cd);
     168           8 :       if (!projectCache.get(cd.project()).map(ProjectState::statePermitsRead).orElse(false)) {
     169           0 :         return false;
     170             :       }
     171             : 
     172           8 :       perm.check(ChangePermission.READ);
     173           8 :       return true;
     174           0 :     } catch (PermissionBackendException | AuthException e) {
     175           0 :       return false;
     176             :     }
     177             :   }
     178             : 
     179             :   private boolean isMagicUser() {
     180           5 :     return account.equals(ChangeQueryBuilder.OWNER_ACCOUNT_ID)
     181           5 :         || account.equals(ChangeQueryBuilder.NON_UPLOADER_ACCOUNT_ID);
     182             :   }
     183             : 
     184             :   @Override
     185             :   public int getCost() {
     186          13 :     return 1 + (group == null ? 0 : 1);
     187             :   }
     188             : }

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