diff options
Diffstat (limited to 'java/com/google/gerrit/server/account/Emails.java')
-rw-r--r-- | java/com/google/gerrit/server/account/Emails.java | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java new file mode 100644 index 0000000000..7c798ca7e4 --- /dev/null +++ b/java/com/google/gerrit/server/account/Emails.java @@ -0,0 +1,114 @@ +// 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.account; + +import static com.google.common.collect.ImmutableSet.toImmutableSet; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Streams; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.server.account.externalids.ExternalId; +import com.google.gerrit.server.account.externalids.ExternalIds; +import com.google.gerrit.server.query.account.InternalAccountQuery; +import com.google.gerrit.server.update.RetryHelper; +import com.google.gerrit.server.update.RetryHelper.Action; +import com.google.gerrit.server.update.RetryHelper.ActionType; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import java.io.IOException; + +/** Class to access accounts by email. */ +@Singleton +public class Emails { + private final ExternalIds externalIds; + private final Provider<InternalAccountQuery> queryProvider; + private final RetryHelper retryHelper; + + @Inject + public Emails( + ExternalIds externalIds, + Provider<InternalAccountQuery> queryProvider, + RetryHelper retryHelper) { + this.externalIds = externalIds; + this.queryProvider = queryProvider; + this.retryHelper = retryHelper; + } + + /** + * Returns the accounts with the given email. + * + * <p>Each email should belong to a single account only. This means if more than one account is + * returned there is an inconsistency in the external IDs. + * + * <p>The accounts are retrieved via the external ID cache. Each access to the external ID cache + * requires reading the SHA1 of the refs/meta/external-ids branch. If accounts for multiple emails + * are needed it is more efficient to use {@link #getAccountsFor(String...)} as this method reads + * the SHA1 of the refs/meta/external-ids branch only once (and not once per email). + * + * <p>In addition accounts are included that have the given email as preferred email even if they + * have no external ID for the preferred email. Having accounts with a preferred email that does + * not exist as external ID is an inconsistency, but existing functionality relies on still + * getting those accounts, which is why they are included. Accounts by preferred email are fetched + * from the account index. + * + * @see #getAccountsFor(String...) + */ + public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException { + return Streams.concat( + externalIds.byEmail(email).stream().map(ExternalId::accountId), + executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream()) + .map(a -> a.getAccount().getId())) + .collect(toImmutableSet()); + } + + /** + * Returns the accounts for the given emails. + * + * @see #getAccountFor(String) + */ + public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails) + throws IOException, OrmException { + ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder(); + externalIds.byEmails(emails).entries().stream() + .forEach(e -> builder.put(e.getKey(), e.getValue().accountId())); + executeIndexQuery(() -> queryProvider.get().byPreferredEmail(emails).entries().stream()) + .forEach(e -> builder.put(e.getKey(), e.getValue().getAccount().getId())); + return builder.build(); + } + + /** + * Returns the accounts with the given email. + * + * <p>This method behaves just like {@link #getAccountFor(String)}, except that accounts are not + * looked up by their preferred email. Thus, this method does not rely on the accounts index. + */ + public ImmutableSet<Account.Id> getAccountForExternal(String email) throws IOException { + return externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet()); + } + + private <T> T executeIndexQuery(Action<T> action) throws OrmException { + try { + return retryHelper.execute(ActionType.INDEX_QUERY, action, OrmException.class::isInstance); + } catch (Exception e) { + Throwables.throwIfUnchecked(e); + Throwables.throwIfInstanceOf(e, OrmException.class); + throw new OrmException(e); + } + } +} |