| // Copyright (C) 2014 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 com.google.common.base.Strings; |
| import com.google.common.collect.ComparisonChain; |
| import com.google.common.collect.Ordering; |
| import com.google.gerrit.extensions.common.AccountInfo; |
| import com.google.gerrit.extensions.restapi.BadRequestException; |
| import com.google.gerrit.extensions.restapi.RestReadView; |
| import com.google.gerrit.extensions.restapi.TopLevelResource; |
| import com.google.gerrit.reviewdb.client.Account; |
| import com.google.gerrit.reviewdb.client.AccountExternalId; |
| import com.google.gerrit.reviewdb.server.ReviewDb; |
| import com.google.gerrit.server.config.GerritServerConfig; |
| import com.google.gwtorm.server.OrmException; |
| import com.google.inject.Inject; |
| |
| import org.eclipse.jgit.lib.Config; |
| import org.kohsuke.args4j.Option; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| class SuggestAccounts implements RestReadView<TopLevelResource> { |
| private static final int MAX_RESULTS = 100; |
| private static final String MAX_SUFFIX = "\u9fa5"; |
| |
| private final AccountControl accountControl; |
| private final AccountLoader accountLoader; |
| private final AccountCache accountCache; |
| private final ReviewDb db; |
| private final boolean suggest; |
| private final int suggestFrom; |
| |
| private int limit = 10; |
| |
| @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of users to return") |
| void setLimit(int n) { |
| if (n < 0) { |
| limit = 10; |
| } else if (n == 0) { |
| limit = MAX_RESULTS; |
| } else { |
| limit = Math.min(n, MAX_RESULTS); |
| } |
| } |
| |
| @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", usage = "match users") |
| private String query; |
| |
| @Inject |
| SuggestAccounts(AccountControl.Factory accountControlFactory, |
| AccountLoader.Factory accountLoaderFactory, |
| AccountCache accountCache, |
| ReviewDb db, |
| @GerritServerConfig Config cfg) { |
| accountControl = accountControlFactory.get(); |
| accountLoader = accountLoaderFactory.create(true); |
| this.accountCache = accountCache; |
| this.db = db; |
| this.suggestFrom = cfg.getInt("suggest", null, "from", 0); |
| |
| if ("off".equalsIgnoreCase(cfg.getString("suggest", null, "accounts"))) { |
| suggest = false; |
| } else { |
| boolean suggest; |
| try { |
| AccountVisibility av = |
| cfg.getEnum("suggest", null, "accounts", AccountVisibility.ALL); |
| suggest = (av != AccountVisibility.NONE); |
| } catch (IllegalArgumentException err) { |
| suggest = cfg.getBoolean("suggest", null, "accounts", true); |
| } |
| this.suggest = suggest; |
| } |
| } |
| |
| @Override |
| public List<AccountInfo> apply(TopLevelResource rsrc) |
| throws OrmException, BadRequestException { |
| if (Strings.isNullOrEmpty(query)) { |
| throw new BadRequestException("missing query field"); |
| } |
| |
| if (!suggest || query.length() < suggestFrom) { |
| return Collections.emptyList(); |
| } |
| |
| String a = query; |
| String b = a + MAX_SUFFIX; |
| |
| Map<Account.Id, AccountInfo> matches = new LinkedHashMap<>(); |
| Map<Account.Id, String> queryEmail = new HashMap<>(); |
| |
| for (Account p : db.accounts().suggestByFullName(a, b, limit)) { |
| addSuggestion(matches, p); |
| } |
| if (matches.size() < limit) { |
| for (Account p : db.accounts() |
| .suggestByPreferredEmail(a, b, limit - matches.size())) { |
| addSuggestion(matches, p); |
| } |
| } |
| if (matches.size() < limit) { |
| for (AccountExternalId e : db.accountExternalIds() |
| .suggestByEmailAddress(a, b, limit - matches.size())) { |
| if (addSuggestion(matches, e.getAccountId())) { |
| queryEmail.put(e.getAccountId(), e.getEmailAddress()); |
| } |
| } |
| } |
| |
| accountLoader.fill(); |
| for (Map.Entry<Account.Id, String> p : queryEmail.entrySet()) { |
| AccountInfo info = matches.get(p.getKey()); |
| if (info != null) { |
| info.email = p.getValue(); |
| } |
| } |
| |
| List<AccountInfo> m = new ArrayList<>(matches.values()); |
| Collections.sort(m, new Comparator<AccountInfo>() { |
| @Override |
| public int compare(AccountInfo a, AccountInfo b) { |
| return ComparisonChain.start() |
| .compare(a.name, b.name, Ordering.natural().nullsLast()) |
| .compare(a.email, b.email, Ordering.natural().nullsLast()) |
| .result(); |
| } |
| }); |
| return m; |
| } |
| |
| private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account a) { |
| if (!a.isActive()) { |
| return false; |
| } |
| Account.Id id = a.getId(); |
| if (!map.containsKey(id) && accountControl.canSee(id)) { |
| map.put(id, accountLoader.get(id)); |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account.Id id) { |
| Account a = accountCache.get(id).getAccount(); |
| return addSuggestion(map, a); |
| } |
| } |