Merge changes I1dcdfb1b,If893d1bd,I7f0383cb

* changes:
  AccountIndex: Add field for exact preferred email
  Return only exact matches from InternalAccountQuery#byPreferredEmail
  AccountIndex: Add fields that allow to detect an account document as stale
This commit is contained in:
Edwin Kempin
2017-08-24 14:38:04 +00:00
committed by Gerrit Code Review
19 changed files with 342 additions and 67 deletions

View File

@@ -709,7 +709,8 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void lookUpByPreferredEmail() throws Exception {
// create an inconsistent account that has a preferred email without external ID
String prefEmail = "foo.preferred@example.com";
String prefix = "foo.preferred";
String prefEmail = prefix + "@example.com";
TestAccount foo = accountCreator.create(name("foo"));
accountsUpdate.create().update(db, foo.id, a -> a.setPreferredEmail(prefEmail));
@@ -717,6 +718,14 @@ public class AccountIT extends AbstractDaemonTest {
ImmutableSet<Account.Id> accountsByPrefEmail = emails.getAccountFor(prefEmail);
assertThat(accountsByPrefEmail).hasSize(1);
assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id);
// look up by email prefix doesn't find the account
accountsByPrefEmail = emails.getAccountFor(prefix);
assertThat(accountsByPrefEmail).isEmpty();
// look up by other case doesn't find the account
accountsByPrefEmail = emails.getAccountFor(prefEmail.toUpperCase(Locale.US));
assertThat(accountsByPrefEmail).isEmpty();
}
@Test

View File

@@ -223,8 +223,6 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
.get()
.byPreferredEmail(email)
.stream()
// the index query also matches prefixes, filter those out
.filter(a -> email.equalsIgnoreCase(a.getAccount().getPreferredEmail()))
.map(AccountState::getAccount)
.findFirst();
return match.isPresent() ? auth(match.get()) : null;

View File

@@ -19,6 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.pgm.init.api.InitStep;
@@ -29,6 +30,7 @@ import com.google.gerrit.reviewdb.client.AccountSshKey;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gwtorm.server.SchemaFactory;
@@ -44,8 +46,9 @@ import java.util.List;
import org.apache.commons.validator.routines.EmailValidator;
public class InitAdminUser implements InitStep {
private final ConsoleUI ui;
private final InitFlags flags;
private final ConsoleUI ui;
private final AllUsersNameOnInitProvider allUsers;
private final AccountsOnInit accounts;
private final VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory;
private final ExternalIdsOnInit externalIds;
@@ -58,6 +61,7 @@ public class InitAdminUser implements InitStep {
InitAdminUser(
InitFlags flags,
ConsoleUI ui,
AllUsersNameOnInitProvider allUsers,
AccountsOnInit accounts,
VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory,
ExternalIdsOnInit externalIds,
@@ -65,6 +69,7 @@ public class InitAdminUser implements InitStep {
GroupsOnInit groupsOnInit) {
this.flags = flags;
this.ui = ui;
this.allUsers = allUsers;
this.accounts = accounts;
this.authorizedKeysFactory = authorizedKeysFactory;
this.externalIds = externalIds;
@@ -128,7 +133,11 @@ public class InitAdminUser implements InitStep {
AccountState as =
new AccountState(
a, Collections.singleton(adminGroup.getGroupUUID()), extIds, new HashMap<>());
new AllUsersName(allUsers.get()),
a,
Collections.singleton(adminGroup.getGroupUUID()),
extIds,
new HashMap<>());
for (AccountIndex accountIndex : indexCollection.getWriteIndexes()) {
accountIndex.replace(as);
}

View File

@@ -30,6 +30,7 @@ import com.google.gerrit.server.account.WatchConfig.NotifyType;
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.group.Groups;
import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.gerrit.server.query.account.InternalAccountQuery;
@@ -76,15 +77,18 @@ public class AccountCacheImpl implements AccountCache {
};
}
private final AllUsersName allUsersName;
private final LoadingCache<Account.Id, Optional<AccountState>> byId;
private final LoadingCache<String, Optional<Account.Id>> byName;
private final Provider<AccountIndexer> indexer;
@Inject
AccountCacheImpl(
AllUsersName allUsersName,
@Named(BYID_NAME) LoadingCache<Account.Id, Optional<AccountState>> byId,
@Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername,
Provider<AccountIndexer> indexer) {
this.allUsersName = allUsersName;
this.byId = byId;
this.byName = byUsername;
this.indexer = indexer;
@@ -142,16 +146,21 @@ public class AccountCacheImpl implements AccountCache {
}
}
private static AccountState missing(Account.Id accountId) {
private AccountState missing(Account.Id accountId) {
Account account = new Account(accountId, TimeUtil.nowTs());
account.setActive(false);
Set<AccountGroup.UUID> anon = ImmutableSet.of();
return new AccountState(
account, anon, Collections.emptySet(), new HashMap<ProjectWatchKey, Set<NotifyType>>());
allUsersName,
account,
anon,
Collections.emptySet(),
new HashMap<ProjectWatchKey, Set<NotifyType>>());
}
static class ByIdLoader extends CacheLoader<Account.Id, Optional<AccountState>> {
private final SchemaFactory<ReviewDb> schema;
private final AllUsersName allUsersName;
private final Accounts accounts;
private final GroupCache groupCache;
private final Groups groups;
@@ -163,6 +172,7 @@ public class AccountCacheImpl implements AccountCache {
@Inject
ByIdLoader(
SchemaFactory<ReviewDb> sf,
AllUsersName allUsersName,
Accounts accounts,
GroupCache groupCache,
Groups groups,
@@ -170,8 +180,9 @@ public class AccountCacheImpl implements AccountCache {
@Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername,
Provider<WatchConfig.Accessor> watchConfig,
ExternalIds externalIds) {
this.accounts = accounts;
this.schema = sf;
this.allUsersName = allUsersName;
this.accounts = accounts;
this.groupCache = groupCache;
this.groups = groups;
this.loader = loader;
@@ -219,6 +230,7 @@ public class AccountCacheImpl implements AccountCache {
return Optional.of(
new AccountState(
allUsersName,
account,
internalGroups,
externalIds.byAccount(who),

View File

@@ -29,6 +29,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.WatchConfig.NotifyType;
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AllUsersName;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
@@ -43,6 +44,7 @@ public class AccountState {
public static final Function<AccountState, Account.Id> ACCOUNT_ID_FUNCTION =
a -> a.getAccount().getId();
private final AllUsersName allUsersName;
private final Account account;
private final Set<AccountGroup.UUID> internalGroups;
private final Collection<ExternalId> externalIds;
@@ -50,10 +52,12 @@ public class AccountState {
private Cache<IdentifiedUser.PropertyKey<Object>, Object> properties;
public AccountState(
AllUsersName allUsersName,
Account account,
Set<AccountGroup.UUID> actualGroups,
Collection<ExternalId> externalIds,
Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
this.allUsersName = allUsersName;
this.account = account;
this.internalGroups = actualGroups;
this.externalIds = externalIds;
@@ -61,6 +65,10 @@ public class AccountState {
this.account.setUserName(getUserName(externalIds));
}
public AllUsersName getAllUsersNameForIndexing() {
return allUsersName;
}
/** Get the cached account metadata. */
public Account getAccount() {
return account;

View File

@@ -27,7 +27,6 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.List;
/** Class to access accounts by email. */
@Singleton
@@ -61,15 +60,9 @@ public class Emails {
* @see #getAccountsFor(String...)
*/
public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException {
List<AccountState> byPreferredEmail = queryProvider.get().byPreferredEmail(email);
return Streams.concat(
externalIds.byEmail(email).stream().map(e -> e.accountId()),
byPreferredEmail
.stream()
// the index query also matches prefixes and emails with other case,
// filter those out
.filter(a -> email.equals(a.getAccount().getPreferredEmail()))
.map(a -> a.getAccount().getId()))
queryProvider.get().byPreferredEmail(email).stream().map(a -> a.getAccount().getId()))
.collect(toImmutableSet());
}
@@ -91,9 +84,6 @@ public class Emails {
.byPreferredEmail(emails)
.entries()
.stream()
// the index query also matches prefixes and emails with other case,
// filter those out
.filter(e -> e.getKey().equals(e.getValue().getAccount().getPreferredEmail()))
.forEach(e -> builder.put(e.getKey(), e.getValue().getAccount().getId()));
return builder.build();
}

View File

@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.hash.Hashing;
@@ -31,6 +32,7 @@ import java.util.Objects;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@AutoValue
@@ -176,7 +178,8 @@ public abstract class ExternalId implements Serializable {
extId.key(), extId.accountId(), extId.email(), extId.password(), blobId);
}
static ExternalId create(
@VisibleForTesting
public static ExternalId create(
Key key,
Account.Id accountId,
@Nullable String email,
@@ -305,6 +308,15 @@ public abstract class ExternalId implements Serializable {
return key().isScheme(scheme);
}
public byte[] toByteArray() {
checkState(blobId() != null, "Missing blobId in external ID %s", key().get());
byte[] b = new byte[2 * Constants.OBJECT_ID_STRING_LENGTH + 1];
key().sha1().copyTo(b, 0);
b[Constants.OBJECT_ID_STRING_LENGTH] = ':';
blobId().copyTo(b, Constants.OBJECT_ID_STRING_LENGTH + 1);
return b;
}
/**
* For checking if two external IDs are equals the blobId is excluded and external IDs that have
* different blob IDs but identical other fields are considered equal. This way an external ID

View File

@@ -0,0 +1,65 @@
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.index;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.auto.value.AutoValue;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Project;
import java.io.IOException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@AutoValue
public abstract class RefState {
public static RefState create(String ref, String sha) {
return new AutoValue_RefState(ref, ObjectId.fromString(sha));
}
public static RefState create(String ref, @Nullable ObjectId id) {
return new AutoValue_RefState(ref, firstNonNull(id, ObjectId.zeroId()));
}
public static RefState of(Ref ref) {
return new AutoValue_RefState(ref.getName(), ref.getObjectId());
}
public byte[] toByteArray(Project.NameKey project) {
byte[] a = (project.toString() + ':' + ref() + ':').getBytes(UTF_8);
byte[] b = new byte[a.length + Constants.OBJECT_ID_STRING_LENGTH];
System.arraycopy(a, 0, b, 0, a.length);
id().copyTo(b, a.length);
return b;
}
public static void check(boolean condition, String str) {
checkArgument(condition, "invalid RefState: %s", str);
}
public abstract String ref();
public abstract ObjectId id();
public boolean match(Repository repo) throws IOException {
Ref ref = repo.exactRef(ref());
ObjectId expected = ref != null ? ref.getObjectId() : ObjectId.zeroId();
return id().equals(expected);
}
}

View File

@@ -17,20 +17,26 @@ package com.google.gerrit.server.index.account;
import static com.google.gerrit.index.FieldDef.exact;
import static com.google.gerrit.index.FieldDef.integer;
import static com.google.gerrit.index.FieldDef.prefix;
import static com.google.gerrit.index.FieldDef.storedOnly;
import static com.google.gerrit.index.FieldDef.timestamp;
import static java.util.stream.Collectors.toSet;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.SchemaUtil;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.index.RefState;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
/** Secondary index schemas for accounts. */
public class AccountField {
@@ -84,6 +90,9 @@ public class AccountField {
return preferredEmail != null ? preferredEmail.toLowerCase() : null;
});
public static final FieldDef<AccountState, String> PREFERRED_EMAIL_EXACT =
exact("preferredemail_exact").build(a -> a.getAccount().getPreferredEmail());
public static final FieldDef<AccountState, Timestamp> REGISTERED =
timestamp("registered").build(a -> a.getAccount().getRegisteredOn());
@@ -98,5 +107,43 @@ public class AccountField {
.transform(k -> k.project().get())
.toSet());
/**
* All values of all refs that were used in the course of indexing this document, except the
* refs/meta/external-ids notes branch which is handled specially (see {@link
* #EXTERNAL_ID_STATE}).
*
* <p>Emitted as UTF-8 encoded strings of the form {@code project:ref/name:[hex sha]}.
*/
public static final FieldDef<AccountState, Iterable<byte[]>> REF_STATE =
storedOnly("ref_state")
.buildRepeatable(
a -> {
if (a.getAccount().getMetaId() == null) {
return ImmutableList.of();
}
return ImmutableList.of(
RefState.create(
RefNames.refsUsers(a.getAccount().getId()),
ObjectId.fromString(a.getAccount().getMetaId()))
.toByteArray(a.getAllUsersNameForIndexing()));
});
/**
* All note values of all external IDs that were used in the course of indexing this document.
*
* <p>Emitted as UTF-8 encoded strings of the form {@code [hex sha of external ID]:[hex sha of
* note blob]}, or with other words {@code [note ID]:[note data ID]}.
*/
public static final FieldDef<AccountState, Iterable<byte[]>> EXTERNAL_ID_STATE =
storedOnly("external_id_state")
.buildRepeatable(
a ->
a.getExternalIds()
.stream()
.filter(e -> e.blobId() != null)
.map(e -> e.toByteArray())
.collect(toSet()));
private AccountField() {}
}

View File

@@ -34,7 +34,13 @@ public class AccountSchemaDefinitions extends SchemaDefinitions<AccountState> {
AccountField.USERNAME,
AccountField.WATCHED_PROJECT);
static final Schema<AccountState> V5 = schema(V4, AccountField.PREFERRED_EMAIL);
@Deprecated static final Schema<AccountState> V5 = schema(V4, AccountField.PREFERRED_EMAIL);
@Deprecated
static final Schema<AccountState> V6 =
schema(V5, AccountField.REF_STATE, AccountField.EXTERNAL_ID_STATE);
static final Schema<AccountState> V7 = schema(V6, AccountField.PREFERRED_EMAIL_EXACT);
public static final String NAME = "accounts";
public static final AccountSchemaDefinitions INSTANCE = new AccountSchemaDefinitions();

View File

@@ -50,7 +50,7 @@ import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.index.change.StalenessChecker.RefState;
import com.google.gerrit.server.index.RefState;
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.server.notedb.ChangeNotes;

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.server.index.change;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -35,6 +34,7 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.RefState;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.query.change.ChangeData;
@@ -47,8 +47,6 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
@@ -221,43 +219,6 @@ public class StalenessChecker {
}
}
@AutoValue
public abstract static class RefState {
static RefState create(String ref, String sha) {
return new AutoValue_StalenessChecker_RefState(ref, ObjectId.fromString(sha));
}
static RefState create(String ref, @Nullable ObjectId id) {
return new AutoValue_StalenessChecker_RefState(ref, firstNonNull(id, ObjectId.zeroId()));
}
static RefState of(Ref ref) {
return new AutoValue_StalenessChecker_RefState(ref.getName(), ref.getObjectId());
}
byte[] toByteArray(Project.NameKey project) {
byte[] a = (project.toString() + ':' + ref() + ':').getBytes(UTF_8);
byte[] b = new byte[a.length + Constants.OBJECT_ID_STRING_LENGTH];
System.arraycopy(a, 0, b, 0, a.length);
id().copyTo(b, a.length);
return b;
}
private static void check(boolean condition, String str) {
checkArgument(condition, "invalid RefState: %s", str);
}
abstract String ref();
abstract ObjectId id();
private boolean match(Repository repo) throws IOException {
Ref ref = repo.exactRef(ref());
ObjectId expected = ref != null ? ref.getObjectId() : ObjectId.zeroId();
return id().equals(expected);
}
}
/**
* Pattern for matching refs.
*

View File

@@ -66,6 +66,11 @@ public class AccountPredicates {
email.toLowerCase());
}
public static Predicate<AccountState> preferredEmailExact(String email) {
return new AccountPredicate(
AccountField.PREFERRED_EMAIL_EXACT, AccountQueryBuilder.FIELD_PREFERRED_EMAIL_EXACT, email);
}
public static Predicate<AccountState> equalsName(String name) {
return new AccountPredicate(
AccountField.NAME_PART, AccountQueryBuilder.FIELD_NAME, name.toLowerCase());

View File

@@ -37,6 +37,7 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState> {
public static final String FIELD_LIMIT = "limit";
public static final String FIELD_NAME = "name";
public static final String FIELD_PREFERRED_EMAIL = "preferredemail";
public static final String FIELD_PREFERRED_EMAIL_EXACT = "preferredemail_exact";
public static final String FIELD_USERNAME = "username";
public static final String FIELD_VISIBLETO = "visibleto";

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.query.account;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
@@ -25,6 +26,7 @@ import com.google.gerrit.index.query.InternalQuery;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -113,17 +115,60 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
return query(AccountPredicates.fullName(fullName));
}
/**
* Queries for accounts that have a preferred email that exactly matches the given email.
*
* @param email preferred email by which accounts should be found
* @return list of accounts that have a preferred email that exactly matches the given email
* @throws OrmException if query cannot be parsed
*/
public List<AccountState> byPreferredEmail(String email) throws OrmException {
return query(AccountPredicates.preferredEmail(email));
if (schema().hasField(AccountField.PREFERRED_EMAIL_EXACT)) {
return query(AccountPredicates.preferredEmailExact(email));
}
return query(AccountPredicates.preferredEmail(email))
.stream()
.filter(a -> a.getAccount().getPreferredEmail().equals(email))
.collect(toList());
}
/**
* Makes multiple queries for accounts by preferred email (exact match).
*
* @param emails preferred emails by which accounts should be found
* @return multimap of the given emails to accounts that have a preferred email that exactly
* matches this email
* @throws OrmException if query cannot be parsed
*/
public Multimap<String, AccountState> byPreferredEmail(String... emails) throws OrmException {
List<String> emailList = Arrays.asList(emails);
if (schema().hasField(AccountField.PREFERRED_EMAIL_EXACT)) {
List<List<AccountState>> r =
query(
emailList
.stream()
.map(e -> AccountPredicates.preferredEmailExact(e))
.collect(toList()));
Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
for (int i = 0; i < emailList.size(); i++) {
accountsByEmail.putAll(emailList.get(i), r.get(i));
}
return accountsByEmail;
}
List<List<AccountState>> r =
query(emailList.stream().map(e -> AccountPredicates.preferredEmail(e)).collect(toList()));
Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
for (int i = 0; i < emailList.size(); i++) {
accountsByEmail.putAll(emailList.get(i), r.get(i));
String email = emailList.get(i);
Set<AccountState> matchingAccounts =
r.get(i)
.stream()
.filter(a -> a.getAccount().getPreferredEmail().equals(email))
.collect(toSet());
accountsByEmail.putAll(email, matchingAccounts);
}
return accountsByEmail;
}

View File

@@ -0,0 +1,94 @@
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.index.account;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.testutil.GerritBaseTests;
import java.util.List;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
public class AccountFieldTest extends GerritBaseTests {
@Test
public void refStateFieldValues() throws Exception {
AllUsersName allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
Account account = new Account(new Account.Id(1), TimeUtil.nowTs());
String metaId = "0e39795bb25dc914118224995c53c5c36923a461";
account.setMetaId(metaId);
List<String> values =
toStrings(
AccountField.REF_STATE.get(
new AccountState(
allUsersName,
account,
ImmutableSet.of(),
ImmutableSet.of(),
ImmutableMap.of())));
assertThat(values).hasSize(1);
String expectedValue =
allUsersName.get() + ":" + RefNames.refsUsers(account.getId()) + ":" + metaId;
assertThat(Iterables.getOnlyElement(values)).isEqualTo(expectedValue);
}
@Test
public void externalIdStateFieldValues() throws Exception {
Account.Id id = new Account.Id(1);
Account account = new Account(id, TimeUtil.nowTs());
ExternalId extId1 =
ExternalId.create(
ExternalId.Key.create(ExternalId.SCHEME_MAILTO, "foo.bar@example.com"),
id,
"foo.bar@example.com",
null,
ObjectId.fromString("1b9a0cf038ea38a0ab08617c39aa8e28413a27ca"));
ExternalId extId2 =
ExternalId.create(
ExternalId.Key.create(ExternalId.SCHEME_USERNAME, "foo"),
id,
null,
"secret",
ObjectId.fromString("5b3a73dc9a668a5b89b5f049225261e3e3291d1a"));
List<String> values =
toStrings(
AccountField.EXTERNAL_ID_STATE.get(
new AccountState(
null,
account,
ImmutableSet.of(),
ImmutableSet.of(extId1, extId2),
ImmutableMap.of())));
String expectedValue1 = extId1.key().sha1().name() + ":" + extId1.blobId().name();
String expectedValue2 = extId2.key().sha1().name() + ":" + extId2.blobId().name();
assertThat(values).containsExactly(expectedValue1, expectedValue2);
}
private List<String> toStrings(Iterable<byte[]> values) {
return Streams.stream(values).map(v -> new String(v, UTF_8)).collect(toList());
}
}

View File

@@ -28,7 +28,7 @@ import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.StalenessChecker.RefState;
import com.google.gerrit.server.index.RefState;
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
import com.google.gerrit.server.notedb.NoteDbChangeState;
import com.google.gerrit.testutil.GerritBaseTests;

View File

@@ -25,6 +25,8 @@ import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.mail.Address;
import java.util.Arrays;
import java.util.Collections;
@@ -383,6 +385,10 @@ public class FromAddressGeneratorProviderTest {
account.setFullName(name);
account.setPreferredEmail(email);
return new AccountState(
account, Collections.emptySet(), Collections.emptySet(), new HashMap<>());
new AllUsersName(AllUsersNameProvider.DEFAULT),
account,
Collections.emptySet(),
Collections.emptySet(),
new HashMap<>());
}
}

View File

@@ -20,6 +20,8 @@ import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import java.util.HashMap;
import java.util.Map;
@@ -78,6 +80,11 @@ public class FakeAccountCache implements AccountCache {
}
private static AccountState newState(Account account) {
return new AccountState(account, ImmutableSet.of(), ImmutableSet.of(), new HashMap<>());
return new AccountState(
new AllUsersName(AllUsersNameProvider.DEFAULT),
account,
ImmutableSet.of(),
ImmutableSet.of(),
new HashMap<>());
}
}