blob: 837e1ede6c9771b0034d59583a5e60b89c4b3294 [file] [log] [blame]
// 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;
public 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;
private String query;
@Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of users to return")
public 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")
public void setQuery(String query) {
this.query = 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);
}
}