LCOV - code coverage report
Current view: top level - server/index/account - AccountField.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 112 113 99.1 %
Date: 2022-11-19 15:00:39 Functions: 19 20 95.0 %

          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.index.account;
      16             : 
      17             : import static java.util.stream.Collectors.toSet;
      18             : 
      19             : import com.google.common.collect.FluentIterable;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.common.collect.Iterables;
      22             : import com.google.gerrit.common.data.GlobalCapability;
      23             : import com.google.gerrit.entities.RefNames;
      24             : import com.google.gerrit.index.IndexedField;
      25             : import com.google.gerrit.index.RefState;
      26             : import com.google.gerrit.index.SchemaUtil;
      27             : import com.google.gerrit.server.account.AccountState;
      28             : import com.google.gerrit.server.account.externalids.ExternalId;
      29             : import com.google.gerrit.server.config.AllUsersName;
      30             : import com.google.gerrit.server.config.AllUsersNameProvider;
      31             : import java.sql.Timestamp;
      32             : import java.util.Arrays;
      33             : import java.util.Collections;
      34             : import java.util.Locale;
      35             : import java.util.Objects;
      36             : import java.util.Set;
      37             : import org.eclipse.jgit.lib.ObjectId;
      38             : 
      39             : /**
      40             :  * Secondary index schemas for accounts.
      41             :  *
      42             :  * <p>Note that this class does not override {@link Object#equals(Object)}. It relies on instances
      43             :  * being singletons so that the default (i.e. reference) comparison works.
      44             :  */
      45             : public class AccountField {
      46             : 
      47         153 :   public static final IndexedField<AccountState, Integer> ID_FIELD =
      48         153 :       IndexedField.<AccountState>integerBuilder("Id")
      49         153 :           .stored()
      50         153 :           .required()
      51         153 :           .build(a -> a.account().id().get());
      52             : 
      53         153 :   public static final IndexedField<AccountState, Integer>.SearchSpec ID_FIELD_SPEC =
      54         153 :       ID_FIELD.integer("id");
      55             : 
      56         153 :   public static final IndexedField<AccountState, String> ID_STR_FIELD =
      57         153 :       IndexedField.<AccountState>stringBuilder("IdStr")
      58         153 :           .stored()
      59         153 :           .required()
      60         153 :           .build(a -> String.valueOf(a.account().id().get()));
      61             : 
      62         153 :   public static final IndexedField<AccountState, String>.SearchSpec ID_STR_FIELD_SPEC =
      63         153 :       ID_STR_FIELD.exact("id_str");
      64             : 
      65             :   /**
      66             :    * External IDs.
      67             :    *
      68             :    * <p>This field includes secondary emails. Use this field only if the current user is allowed to
      69             :    * see secondary emails (requires the {@link GlobalCapability#MODIFY_ACCOUNT} capability).
      70             :    */
      71         153 :   public static final IndexedField<AccountState, Iterable<String>> EXTERNAL_ID_FIELD =
      72         153 :       IndexedField.<AccountState>iterableStringBuilder("ExternalId")
      73         153 :           .required()
      74         153 :           .build(a -> Iterables.transform(a.externalIds(), id -> id.key().get()));
      75             : 
      76             :   public static final IndexedField<AccountState, Iterable<String>>.SearchSpec
      77         153 :       EXTERNAL_ID_FIELD_SPEC = EXTERNAL_ID_FIELD.exact("external_id");
      78             : 
      79             :   /**
      80             :    * Fuzzy prefix match on name and email parts.
      81             :    *
      82             :    * <p>This field includes parts from the secondary emails. Use this field only if the current user
      83             :    * is allowed to see secondary emails (requires the {@link GlobalCapability#MODIFY_ACCOUNT}
      84             :    * capability).
      85             :    *
      86             :    * <p>Use the {@link AccountField#NAME_PART_NO_SECONDARY_EMAIL_SPEC} if the current user can't see
      87             :    * secondary emails.
      88             :    */
      89         153 :   public static final IndexedField<AccountState, Iterable<String>> NAME_PART_FIELD =
      90         153 :       IndexedField.<AccountState>iterableStringBuilder("FullNameAndAllEmailsParts")
      91         153 :           .description("Full name, all linked emails and their parts (split at special characters)")
      92         153 :           .required()
      93         153 :           .build(a -> getNameParts(a, Iterables.transform(a.externalIds(), ExternalId::email)));
      94             : 
      95         153 :   public static final IndexedField<AccountState, Iterable<String>>.SearchSpec NAME_PART_SPEC =
      96         153 :       NAME_PART_FIELD.prefix("name");
      97             : 
      98             :   /**
      99             :    * Fuzzy prefix match on name and preferred email parts. Parts of secondary emails are not
     100             :    * included.
     101             :    */
     102             :   public static final IndexedField<AccountState, Iterable<String>>
     103         153 :       NAME_PART_NO_SECONDARY_EMAIL_FIELD =
     104         153 :           IndexedField.<AccountState>iterableStringBuilder("FullNameAndPreferredEmailParts")
     105         153 :               .description(
     106             :                   "Full name, preferred emails and its parts (split at special characters)")
     107         153 :               .required()
     108         153 :               .build(a -> getNameParts(a, Arrays.asList(a.account().preferredEmail())));
     109             : 
     110             :   public static final IndexedField<AccountState, Iterable<String>>.SearchSpec
     111         153 :       NAME_PART_NO_SECONDARY_EMAIL_SPEC = NAME_PART_NO_SECONDARY_EMAIL_FIELD.prefix("name2");
     112             : 
     113         153 :   public static final IndexedField<AccountState, String> FULL_NAME_FIELD =
     114         153 :       IndexedField.<AccountState>stringBuilder("FullName").build(a -> a.account().fullName());
     115             : 
     116         153 :   public static final IndexedField<AccountState, String>.SearchSpec FULL_NAME_SPEC =
     117         153 :       FULL_NAME_FIELD.exact("full_name");
     118             : 
     119         153 :   public static final IndexedField<AccountState, String> ACTIVE_FIELD =
     120         153 :       IndexedField.<AccountState>stringBuilder("Active")
     121         153 :           .required()
     122         153 :           .build(a -> a.account().isActive() ? "1" : "0");
     123             : 
     124         153 :   public static final IndexedField<AccountState, String>.SearchSpec ACTIVE_FIELD_SPEC =
     125         153 :       ACTIVE_FIELD.exact("inactive");
     126             :   /**
     127             :    * All emails (preferred email + secondary emails). Use this field only if the current user is
     128             :    * allowed to see secondary emails (requires the 'Modify Account' capability).
     129             :    *
     130             :    * <p>Use the {@link AccountField#PREFERRED_EMAIL_LOWER_CASE_SPEC} if the current user can't see
     131             :    * secondary emails.
     132             :    */
     133         153 :   public static final IndexedField<AccountState, Iterable<String>> EMAIL_FIELD =
     134         153 :       IndexedField.<AccountState>iterableStringBuilder("Email")
     135         153 :           .required()
     136         153 :           .build(
     137             :               a ->
     138          10 :                   FluentIterable.from(a.externalIds())
     139          10 :                       .transform(ExternalId::email)
     140          10 :                       .append(Collections.singleton(a.account().preferredEmail()))
     141          10 :                       .filter(Objects::nonNull)
     142          10 :                       .transform(String::toLowerCase)
     143          10 :                       .toSet());
     144             : 
     145         153 :   public static final IndexedField<AccountState, Iterable<String>>.SearchSpec EMAIL_SPEC =
     146         153 :       EMAIL_FIELD.prefix("email");
     147             : 
     148         153 :   public static final IndexedField<AccountState, String> PREFERRED_EMAIL_LOWER_CASE_FIELD =
     149         153 :       IndexedField.<AccountState>stringBuilder("PreferredEmailLowerCase")
     150         153 :           .build(
     151             :               a -> {
     152          10 :                 String preferredEmail = a.account().preferredEmail();
     153          10 :                 return preferredEmail != null ? preferredEmail.toLowerCase() : null;
     154             :               });
     155             : 
     156             :   public static final IndexedField<AccountState, String>.SearchSpec
     157         153 :       PREFERRED_EMAIL_LOWER_CASE_SPEC = PREFERRED_EMAIL_LOWER_CASE_FIELD.prefix("preferredemail");
     158             : 
     159         153 :   public static final IndexedField<AccountState, String> PREFERRED_EMAIL_EXACT_FIELD =
     160         153 :       IndexedField.<AccountState>stringBuilder("PreferredEmail")
     161         153 :           .build(a -> a.account().preferredEmail());
     162             : 
     163         153 :   public static final IndexedField<AccountState, String>.SearchSpec PREFERRED_EMAIL_EXACT_SPEC =
     164         153 :       PREFERRED_EMAIL_EXACT_FIELD.exact("preferredemail_exact");
     165             : 
     166             :   // TODO(issue-15518): Migrate type for timestamp index fields from Timestamp to Instant
     167         153 :   public static final IndexedField<AccountState, Timestamp> REGISTERED_FIELD =
     168         153 :       IndexedField.<AccountState>timestampBuilder("Registered")
     169         153 :           .required()
     170         153 :           .build(a -> Timestamp.from(a.account().registeredOn()));
     171             : 
     172         153 :   public static final IndexedField<AccountState, Timestamp>.SearchSpec REGISTERED_SPEC =
     173         153 :       REGISTERED_FIELD.timestamp("registered");
     174             : 
     175         153 :   public static final IndexedField<AccountState, String> USERNAME_FIELD =
     176         153 :       IndexedField.<AccountState>stringBuilder("Username")
     177         153 :           .build(a -> a.userName().map(String::toLowerCase).orElse(""));
     178             : 
     179         153 :   public static final IndexedField<AccountState, String>.SearchSpec USERNAME_SPEC =
     180         153 :       USERNAME_FIELD.exact("username");
     181             : 
     182         153 :   public static final IndexedField<AccountState, Iterable<String>> WATCHED_PROJECT_FIELD =
     183         153 :       IndexedField.<AccountState>iterableStringBuilder("WatchedProject")
     184         153 :           .build(
     185             :               a ->
     186         105 :                   FluentIterable.from(a.projectWatches().keySet())
     187         105 :                       .transform(k -> k.project().get())
     188         105 :                       .toSet());
     189             : 
     190         153 :   public static final IndexedField<AccountState, Iterable<String>>.SearchSpec WATCHED_PROJECT_SPEC =
     191         153 :       WATCHED_PROJECT_FIELD.exact("watchedproject");
     192             : 
     193             :   /**
     194             :    * All values of all refs that were used in the course of indexing this document, except the
     195             :    * refs/meta/external-ids notes branch which is handled specially (see {@link
     196             :    * #EXTERNAL_ID_STATE_SPEC}).
     197             :    *
     198             :    * <p>Emitted as UTF-8 encoded strings of the form {@code project:ref/name:[hex sha]}.
     199             :    */
     200         153 :   public static final IndexedField<AccountState, Iterable<byte[]>> REF_STATE_FIELD =
     201         153 :       IndexedField.<AccountState>iterableByteArrayBuilder("RefState")
     202         153 :           .stored()
     203         153 :           .required()
     204         153 :           .build(
     205             :               a -> {
     206          11 :                 if (a.account().metaId() == null) {
     207           0 :                   return ImmutableList.of();
     208             :                 }
     209             : 
     210          11 :                 return ImmutableList.of(
     211          11 :                     RefState.create(
     212          11 :                             RefNames.refsUsers(a.account().id()),
     213          11 :                             ObjectId.fromString(a.account().metaId()))
     214             :                         // We use the default AllUsers name to avoid having to pass around that
     215             :                         // variable just for indexing.
     216             :                         // This field is only used for staleness detection which will discover the
     217             :                         // default name and replace it with the actually configured name.
     218          11 :                         .toByteArray(new AllUsersName(AllUsersNameProvider.DEFAULT)));
     219             :               });
     220             : 
     221         153 :   public static final IndexedField<AccountState, Iterable<byte[]>>.SearchSpec REF_STATE_SPEC =
     222         153 :       REF_STATE_FIELD.storedOnly("ref_state");
     223             : 
     224             :   /**
     225             :    * All note values of all external IDs that were used in the course of indexing this document.
     226             :    *
     227             :    * <p>Emitted as UTF-8 encoded strings of the form {@code [hex sha of external ID]:[hex sha of
     228             :    * note blob]}, or with other words {@code [note ID]:[note data ID]}.
     229             :    */
     230         153 :   public static final IndexedField<AccountState, Iterable<byte[]>> EXTERNAL_ID_STATE_FIELD =
     231         153 :       IndexedField.<AccountState>iterableByteArrayBuilder("ExternalIdState")
     232         153 :           .stored()
     233         153 :           .required()
     234         153 :           .build(
     235             :               a ->
     236          11 :                   a.externalIds().stream()
     237          11 :                       .filter(e -> e.blobId() != null)
     238          11 :                       .map(ExternalId::toByteArray)
     239          11 :                       .collect(toSet()));
     240             : 
     241             :   public static final IndexedField<AccountState, Iterable<byte[]>>.SearchSpec
     242         153 :       EXTERNAL_ID_STATE_SPEC = EXTERNAL_ID_STATE_FIELD.storedOnly("external_id_state");
     243             : 
     244             :   private static final Set<String> getNameParts(AccountState a, Iterable<String> emails) {
     245          35 :     String fullName = a.account().fullName();
     246          35 :     Set<String> parts = SchemaUtil.getNameParts(fullName, emails);
     247             : 
     248             :     // Additional values not currently added by getPersonParts.
     249             :     // TODO(dborowitz): Move to getPersonParts and remove this hack.
     250          35 :     if (fullName != null) {
     251          35 :       parts.add(fullName.toLowerCase(Locale.US));
     252             :     }
     253          35 :     return parts;
     254             :   }
     255             : 
     256             :   private AccountField() {}
     257             : }

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