Set '_more_accounts' on last account of query result

If the number of accounts matching the query exceeds the limit the
last account has a '_more_accounts: true' JSON field set.

Change-Id: Icc8c34c37c72f7df00bb06c67602bea36a4ae09f
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
Edwin Kempin
2016-06-30 15:29:05 +02:00
parent 6115eae5d0
commit 7bb93e4601
4 changed files with 45 additions and 31 deletions

View File

@@ -23,7 +23,7 @@ returned.
.Request .Request
---- ----
GET /accounts/?q=name:John+email:example.com HTTP/1.0 GET /accounts/?q=name:John+email:example.com&n=2 HTTP/1.0
---- ----
.Response .Response
@@ -44,11 +44,16 @@ returned.
"_account_id": 1001439, "_account_id": 1001439,
"name": "John Smith", "name": "John Smith",
"email": "john.smith@example.com", "email": "john.smith@example.com",
"username": "jsmith" "username": "jsmith",
"_more_accounts": true
} }
] ]
---- ----
If the number of accounts matching the query exceeds either the
internal limit or a supplied `n` query parameter, the last account
object has a `_more_accounts: true` JSON field set.
The `S` or `start` query parameter can be supplied to skip a number The `S` or `start` query parameter can be supplied to skip a number
of accounts from the list. of accounts from the list.
@@ -1938,20 +1943,23 @@ registered.
The `AccountInfo` entity contains information about an account. The `AccountInfo` entity contains information about an account.
[options="header",cols="1,^1,5"] [options="header",cols="1,^1,5"]
|=========================== |=============================
|Field Name ||Description |Field Name ||Description
|`_account_id` ||The numeric ID of the account. |`_account_id` ||The numeric ID of the account.
|`name` |optional|The full name of the user. + |`name` |optional|The full name of the user. +
Only set if link:rest-api-changes.html#detailed-accounts[detailed Only set if link:rest-api-changes.html#detailed-accounts[detailed
account information] is requested. account information] is requested.
|`email` |optional| |`email` |optional|
The email address the user prefers to be contacted through. + The email address the user prefers to be contacted through. +
Only set if link:rest-api-changes.html#detailed-accounts[detailed Only set if link:rest-api-changes.html#detailed-accounts[detailed
account information] is requested. account information] is requested.
|`username` |optional|The username of the user. + |`username` |optional|The username of the user. +
Only set if link:rest-api-changes.html#detailed-accounts[detailed Only set if link:rest-api-changes.html#detailed-accounts[detailed
account information] is requested. account information] is requested.
|=========================== |`_more_accounts`|optional, not set if `false`|
Whether the query would deliver more results if not limited. +
Only set on the last account that is returned.
|=============================
[[account-input]] [[account-input]]
=== AccountInput === AccountInput

View File

@@ -22,6 +22,7 @@ public class AccountInfo {
public String email; public String email;
public String username; public String username;
public List<AvatarInfo> avatars; public List<AvatarInfo> avatars;
public Boolean _moreAccounts;
public AccountInfo(Integer id) { public AccountInfo(Integer id) {
this._accountId = id; this._accountId = id;

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.server.account; package com.google.gerrit.server.account;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -39,7 +39,6 @@ import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.kohsuke.args4j.Option; import org.kohsuke.args4j.Option;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -139,23 +138,20 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
} }
AccountIndex searchIndex = indexes.getSearchIndex(); AccountIndex searchIndex = indexes.getSearchIndex();
Collection<AccountInfo> matches;
if (searchIndex != null) { if (searchIndex != null) {
matches = queryFromIndex(); return queryFromIndex();
} else {
if (!suggest) {
throw new MethodNotAllowedException();
}
if (start != null) {
throw new MethodNotAllowedException("option start not allowed");
}
matches = queryFromDb();
} }
return AccountInfoComparator.ORDER_NULLS_LAST.sortedCopy(matches); if (!suggest) {
throw new MethodNotAllowedException();
}
if (start != null) {
throw new MethodNotAllowedException("option start not allowed");
}
return queryFromDb();
} }
public Collection<AccountInfo> queryFromIndex() public List<AccountInfo> queryFromIndex()
throws BadRequestException, MethodNotAllowedException, OrmException { throws BadRequestException, MethodNotAllowedException, OrmException {
if (queryProcessor.isDisabled()) { if (queryProcessor.isDisabled()) {
throw new MethodNotAllowedException("query disabled"); throw new MethodNotAllowedException("query disabled");
@@ -179,18 +175,24 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
Account.Id id = accountState.getAccount().getId(); Account.Id id = accountState.getAccount().getId();
matches.put(id, accountLoader.get(id)); matches.put(id, accountLoader.get(id));
} }
accountLoader.fill();
List<AccountInfo> sorted =
AccountInfoComparator.ORDER_NULLS_LAST.sortedCopy(matches.values());
if (!sorted.isEmpty() && result.more()) {
sorted.get(sorted.size() - 1)._moreAccounts = true;
}
return sorted;
} catch (QueryParseException e) { } catch (QueryParseException e) {
if (suggest) { if (suggest) {
return ImmutableSet.of(); return ImmutableList.of();
} }
throw new BadRequestException(e.getMessage()); throw new BadRequestException(e.getMessage());
} }
accountLoader.fill();
return matches.values();
} }
public Collection<AccountInfo> queryFromDb() throws OrmException { public List<AccountInfo> queryFromDb() throws OrmException {
String a = query; String a = query;
String b = a + MAX_SUFFIX; String b = a + MAX_SUFFIX;
@@ -223,7 +225,7 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
} }
} }
return matches.values(); return AccountInfoComparator.ORDER_NULLS_LAST.sortedCopy(matches.values());
} }
private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account a) { private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account a) {

View File

@@ -252,8 +252,11 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain); AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain);
AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain); AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain);
assertQuery(domain, user1, user2, user3); List<AccountInfo> result = assertQuery(domain, user1, user2, user3);
assertQuery(newQuery(domain).withLimit(2), user1, user2); assertThat(result.get(result.size() - 1)._moreAccounts).isNull();
result = assertQuery(newQuery(domain).withLimit(2), user1, user2);
assertThat(result.get(result.size() - 1)._moreAccounts).isTrue();
} }
@Test @Test