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.query.account; 16 : 17 : import static java.util.stream.Collectors.toList; 18 : import static java.util.stream.Collectors.toSet; 19 : 20 : import com.google.common.base.Joiner; 21 : import com.google.common.collect.ArrayListMultimap; 22 : import com.google.common.collect.ImmutableList; 23 : import com.google.common.collect.ImmutableListMultimap; 24 : import com.google.common.collect.Multimap; 25 : import com.google.common.flogger.FluentLogger; 26 : import com.google.gerrit.common.Nullable; 27 : import com.google.gerrit.common.UsedAt; 28 : import com.google.gerrit.entities.Project; 29 : import com.google.gerrit.index.IndexConfig; 30 : import com.google.gerrit.index.Schema; 31 : import com.google.gerrit.index.SchemaFieldDefs.SchemaField; 32 : import com.google.gerrit.index.query.InternalQuery; 33 : import com.google.gerrit.server.account.AccountState; 34 : import com.google.gerrit.server.account.externalids.ExternalId; 35 : import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory; 36 : import com.google.gerrit.server.index.account.AccountField; 37 : import com.google.gerrit.server.index.account.AccountIndexCollection; 38 : import com.google.inject.Inject; 39 : import java.util.List; 40 : import java.util.Set; 41 : 42 : /** 43 : * Query wrapper for the account index. 44 : * 45 : * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than 46 : * holding on to a single instance. 47 : */ 48 : public class InternalAccountQuery extends InternalQuery<AccountState, InternalAccountQuery> { 49 108 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 50 : 51 : private final ExternalIdKeyFactory externalIdKeyFactory; 52 : 53 : @Inject 54 : InternalAccountQuery( 55 : AccountQueryProcessor queryProcessor, 56 : AccountIndexCollection indexes, 57 : IndexConfig indexConfig, 58 : ExternalIdKeyFactory externalIdKeyFactory) { 59 108 : super(queryProcessor, indexes, indexConfig); 60 108 : this.externalIdKeyFactory = externalIdKeyFactory; 61 108 : } 62 : 63 : public List<AccountState> byDefault(String query, boolean canSeeSecondaryEmails) { 64 31 : return query(AccountPredicates.defaultPredicate(schema(), canSeeSecondaryEmails, query)); 65 : } 66 : 67 : public List<AccountState> byExternalId(String scheme, String id) { 68 0 : return byExternalId(externalIdKeyFactory.create(scheme, id)); 69 : } 70 : 71 : public List<AccountState> byExternalId(ExternalId.Key externalId) { 72 3 : return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString())); 73 : } 74 : 75 : @Nullable 76 : @UsedAt(UsedAt.Project.COLLABNET) 77 : public AccountState oneByExternalId(ExternalId.Key externalId) { 78 0 : List<AccountState> accountStates = byExternalId(externalId); 79 0 : if (accountStates.size() == 1) { 80 0 : return accountStates.get(0); 81 0 : } else if (!accountStates.isEmpty()) { 82 0 : StringBuilder msg = new StringBuilder(); 83 0 : msg.append("Ambiguous external ID ").append(externalId).append(" for accounts: "); 84 0 : Joiner.on(", ") 85 0 : .appendTo( 86 0 : msg, accountStates.stream().map(a -> a.account().id().toString()).collect(toList())); 87 0 : logger.atWarning().log("%s", msg); 88 : } 89 0 : return null; 90 : } 91 : 92 : public List<AccountState> byFullName(String fullName) { 93 33 : return query(AccountPredicates.fullName(fullName)); 94 : } 95 : 96 : /** 97 : * Queries for accounts that have a preferred email that exactly matches the given email. 98 : * 99 : * @param email preferred email by which accounts should be found 100 : * @return list of accounts that have a preferred email that exactly matches the given email 101 : */ 102 : public List<AccountState> byPreferredEmail(String email) { 103 36 : if (hasPreferredEmailExact()) { 104 36 : return query(AccountPredicates.preferredEmailExact(email)); 105 : } 106 : 107 0 : if (!hasPreferredEmail()) { 108 0 : return ImmutableList.of(); 109 : } 110 : 111 0 : return query(AccountPredicates.preferredEmail(email)).stream() 112 0 : .filter(a -> a.account().preferredEmail().equals(email)) 113 0 : .collect(toList()); 114 : } 115 : 116 : /** 117 : * Makes multiple queries for accounts by preferred email (exact match). 118 : * 119 : * @param emails preferred emails by which accounts should be found 120 : * @return multimap of the given emails to accounts that have a preferred email that exactly 121 : * matches this email 122 : */ 123 : public Multimap<String, AccountState> byPreferredEmail(List<String> emails) { 124 0 : if (hasPreferredEmailExact()) { 125 0 : List<List<AccountState>> r = 126 0 : query(emails.stream().map(AccountPredicates::preferredEmailExact).collect(toList())); 127 0 : Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create(); 128 0 : for (int i = 0; i < emails.size(); i++) { 129 0 : accountsByEmail.putAll(emails.get(i), r.get(i)); 130 : } 131 0 : return accountsByEmail; 132 : } 133 : 134 0 : if (!hasPreferredEmail()) { 135 0 : return ImmutableListMultimap.of(); 136 : } 137 : 138 0 : List<List<AccountState>> r = 139 0 : query(emails.stream().map(AccountPredicates::preferredEmail).collect(toList())); 140 0 : Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create(); 141 0 : for (int i = 0; i < emails.size(); i++) { 142 0 : String email = emails.get(i); 143 0 : Set<AccountState> matchingAccounts = 144 0 : r.get(i).stream() 145 0 : .filter(a -> a.account().preferredEmail().equals(email)) 146 0 : .collect(toSet()); 147 0 : accountsByEmail.putAll(email, matchingAccounts); 148 : } 149 0 : return accountsByEmail; 150 : } 151 : 152 : public List<AccountState> byWatchedProject(Project.NameKey project) { 153 103 : return query(AccountPredicates.watchedProject(project)); 154 : } 155 : 156 : private boolean hasField(SchemaField<AccountState, ?> field) { 157 36 : Schema<AccountState> s = schema(); 158 36 : return (s != null && s.hasField(field)); 159 : } 160 : 161 : private boolean hasPreferredEmail() { 162 0 : return hasField(AccountField.PREFERRED_EMAIL_LOWER_CASE_SPEC); 163 : } 164 : 165 : private boolean hasPreferredEmailExact() { 166 36 : return hasField(AccountField.PREFERRED_EMAIL_EXACT_SPEC); 167 : } 168 : }