Adds group suggestions into ListGroups REST API.
This is a different approach of Ie86de1f73ccd7bdec041730d95301d6aa3bdbdc4. This allows group auto completion to be used in a plugin's UI. Change-Id: Ia1cfa068246127c29f1b74f6aa4562a9167c301e
This commit is contained in:
parent
b5032e1aef
commit
8c48525834
@ -172,6 +172,46 @@ Query 25 groups starting from index 50.
|
|||||||
GET /groups/?n=25&S=50 HTTP/1.0
|
GET /groups/?n=25&S=50 HTTP/1.0
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[suggest-group]]
|
||||||
|
==== Suggest Group
|
||||||
|
The `suggest` option indicates a user-entered string that
|
||||||
|
should be auto-completed to group names.
|
||||||
|
If this option is set and `n` is not set, then `n` defaults to 10.
|
||||||
|
|
||||||
|
When using this option,
|
||||||
|
the `project` or `p` option can be used to name the current project,
|
||||||
|
to allow context-dependent suggestions.
|
||||||
|
|
||||||
|
Not compatible with `visible-to-all`, `owned`, `user`, `match`, `q`,
|
||||||
|
or `S`.
|
||||||
|
(Attempts to use one of those options combined with `suggest` will
|
||||||
|
error out.)
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
GET /groups/?suggest=ad&p=All-Projects HTTP/1.0
|
||||||
|
----
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json; charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"Administrators": {
|
||||||
|
"url": "#/admin/groups/uuid-59b92f35489e62c80d1ab1bf0c2d17843038df8b",
|
||||||
|
"options": {},
|
||||||
|
"description": "Gerrit Site Administrators",
|
||||||
|
"group_id": 1,
|
||||||
|
"owner": "Administrators",
|
||||||
|
"owner_id": "59b92f35489e62c80d1ab1bf0c2d17843038df8b",
|
||||||
|
"id": "59b92f35489e62c80d1ab1bf0c2d17843038df8b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
[[get-group]]
|
[[get-group]]
|
||||||
=== Get Group
|
=== Get Group
|
||||||
--
|
--
|
||||||
|
@ -401,6 +401,13 @@ public class GroupsIT extends AbstractDaemonTest {
|
|||||||
assertThat(gApi.groups().list().getAsMap()).containsKey(newGroupName);
|
assertThat(gApi.groups().list().getAsMap()).containsKey(newGroupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuggestGroup() throws Exception {
|
||||||
|
Map<String, GroupInfo> groups = gApi.groups().list().withSuggest("adm").getAsMap();
|
||||||
|
assertThat(groups).containsKey("Administrators");
|
||||||
|
assertThat(groups).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllGroupInfoFieldsSetCorrectly() throws Exception {
|
public void testAllGroupInfoFieldsSetCorrectly() throws Exception {
|
||||||
AccountGroup adminGroup = getFromCache("Administrators");
|
AccountGroup adminGroup = getFromCache("Administrators");
|
||||||
|
@ -63,6 +63,7 @@ public interface Groups {
|
|||||||
private int limit;
|
private int limit;
|
||||||
private int start;
|
private int start;
|
||||||
private String substring;
|
private String substring;
|
||||||
|
private String suggest;
|
||||||
|
|
||||||
public List<GroupInfo> get() throws RestApiException {
|
public List<GroupInfo> get() throws RestApiException {
|
||||||
Map<String, GroupInfo> map = getAsMap();
|
Map<String, GroupInfo> map = getAsMap();
|
||||||
@ -128,6 +129,11 @@ public interface Groups {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListRequest withSuggest(String suggest) {
|
||||||
|
this.suggest = suggest;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public EnumSet<ListGroupsOption> getOptions() {
|
public EnumSet<ListGroupsOption> getOptions() {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
@ -163,5 +169,9 @@ public interface Groups {
|
|||||||
public String getSubstring() {
|
public String getSubstring() {
|
||||||
return substring;
|
return substring;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSuggest() {
|
||||||
|
return suggest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,7 @@ class GroupsImpl implements Groups {
|
|||||||
list.setLimit(req.getLimit());
|
list.setLimit(req.getLimit());
|
||||||
list.setStart(req.getStart());
|
list.setStart(req.getStart());
|
||||||
list.setMatchSubstring(req.getSubstring());
|
list.setMatchSubstring(req.getSubstring());
|
||||||
|
list.setSuggest(req.getSuggest());
|
||||||
try {
|
try {
|
||||||
return list.apply(tlr);
|
return list.apply(tlr);
|
||||||
} catch (OrmException e) {
|
} catch (OrmException e) {
|
||||||
|
@ -16,14 +16,18 @@ package com.google.gerrit.server.group;
|
|||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.common.data.GroupDescription;
|
||||||
import com.google.gerrit.common.data.GroupDescriptions;
|
import com.google.gerrit.common.data.GroupDescriptions;
|
||||||
import com.google.gerrit.common.data.GroupReference;
|
import com.google.gerrit.common.data.GroupReference;
|
||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||||
import com.google.gerrit.extensions.client.ListGroupsOption;
|
import com.google.gerrit.extensions.client.ListGroupsOption;
|
||||||
import com.google.gerrit.extensions.common.GroupInfo;
|
import com.google.gerrit.extensions.common.GroupInfo;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
import com.google.gerrit.extensions.restapi.Url;
|
import com.google.gerrit.extensions.restapi.Url;
|
||||||
@ -32,6 +36,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
|||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AccountResource;
|
import com.google.gerrit.server.account.AccountResource;
|
||||||
import com.google.gerrit.server.account.GetGroups;
|
import com.google.gerrit.server.account.GetGroups;
|
||||||
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.account.GroupComparator;
|
import com.google.gerrit.server.account.GroupComparator;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
@ -64,6 +69,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
private final IdentifiedUser.GenericFactory userFactory;
|
private final IdentifiedUser.GenericFactory userFactory;
|
||||||
private final Provider<GetGroups> accountGetGroups;
|
private final Provider<GetGroups> accountGetGroups;
|
||||||
private final GroupJson json;
|
private final GroupJson json;
|
||||||
|
private final GroupBackend groupBackend;
|
||||||
|
|
||||||
private EnumSet<ListGroupsOption> options =
|
private EnumSet<ListGroupsOption> options =
|
||||||
EnumSet.noneOf(ListGroupsOption.class);
|
EnumSet.noneOf(ListGroupsOption.class);
|
||||||
@ -73,6 +79,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
private int limit;
|
private int limit;
|
||||||
private int start;
|
private int start;
|
||||||
private String matchSubstring;
|
private String matchSubstring;
|
||||||
|
private String suggest;
|
||||||
|
|
||||||
@Option(name = "--project", aliases = {"-p"},
|
@Option(name = "--project", aliases = {"-p"},
|
||||||
usage = "projects for which the groups should be listed")
|
usage = "projects for which the groups should be listed")
|
||||||
@ -121,6 +128,11 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
this.matchSubstring = matchSubstring;
|
this.matchSubstring = matchSubstring;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Option(name = "--suggest", usage = "to get a suggestion of groups")
|
||||||
|
public void setSuggest(String suggest) {
|
||||||
|
this.suggest = suggest;
|
||||||
|
}
|
||||||
|
|
||||||
@Option(name = "-o", usage = "Output options per group")
|
@Option(name = "-o", usage = "Output options per group")
|
||||||
void addOption(ListGroupsOption o) {
|
void addOption(ListGroupsOption o) {
|
||||||
options.add(o);
|
options.add(o);
|
||||||
@ -137,7 +149,8 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
final GroupControl.GenericFactory genericGroupControlFactory,
|
final GroupControl.GenericFactory genericGroupControlFactory,
|
||||||
final Provider<IdentifiedUser> identifiedUser,
|
final Provider<IdentifiedUser> identifiedUser,
|
||||||
final IdentifiedUser.GenericFactory userFactory,
|
final IdentifiedUser.GenericFactory userFactory,
|
||||||
final Provider<GetGroups> accountGetGroups, GroupJson json) {
|
final Provider<GetGroups> accountGetGroups, GroupJson json,
|
||||||
|
GroupBackend groupBackend) {
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.groupControlFactory = groupControlFactory;
|
this.groupControlFactory = groupControlFactory;
|
||||||
this.genericGroupControlFactory = genericGroupControlFactory;
|
this.genericGroupControlFactory = genericGroupControlFactory;
|
||||||
@ -145,6 +158,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
this.userFactory = userFactory;
|
this.userFactory = userFactory;
|
||||||
this.accountGetGroups = accountGetGroups;
|
this.accountGetGroups = accountGetGroups;
|
||||||
this.json = json;
|
this.json = json;
|
||||||
|
this.groupBackend = groupBackend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOptions(EnumSet<ListGroupsOption> options) {
|
public void setOptions(EnumSet<ListGroupsOption> options) {
|
||||||
@ -161,7 +175,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
|
public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
|
||||||
throws OrmException {
|
throws OrmException, BadRequestException {
|
||||||
SortedMap<String, GroupInfo> output = Maps.newTreeMap();
|
SortedMap<String, GroupInfo> output = Maps.newTreeMap();
|
||||||
for (GroupInfo info : get()) {
|
for (GroupInfo info : get()) {
|
||||||
output.put(MoreObjects.firstNonNull(
|
output.put(MoreObjects.firstNonNull(
|
||||||
@ -172,19 +186,26 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GroupInfo> get() throws OrmException {
|
public List<GroupInfo> get() throws OrmException, BadRequestException {
|
||||||
List<GroupInfo> groupInfos;
|
if (!Strings.isNullOrEmpty(suggest)) {
|
||||||
if (user != null) {
|
return suggestGroups();
|
||||||
|
}
|
||||||
|
|
||||||
if (owned) {
|
if (owned) {
|
||||||
groupInfos = getGroupsOwnedBy(userFactory.create(user));
|
return getGroupsOwnedBy(
|
||||||
} else {
|
user != null ? userFactory.create(user) : identifiedUser.get());
|
||||||
groupInfos = accountGetGroups.get().apply(
|
}
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
return accountGetGroups.get().apply(
|
||||||
new AccountResource(userFactory.create(user)));
|
new AccountResource(userFactory.create(user)));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (owned) {
|
return getAllGroups();
|
||||||
groupInfos = getGroupsOwnedBy(identifiedUser.get());
|
}
|
||||||
} else {
|
|
||||||
|
private List<GroupInfo> getAllGroups() throws OrmException {
|
||||||
|
List<GroupInfo> groupInfos;
|
||||||
List<AccountGroup> groupList;
|
List<AccountGroup> groupList;
|
||||||
if (!projects.isEmpty()) {
|
if (!projects.isEmpty()) {
|
||||||
Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap();
|
Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap();
|
||||||
@ -214,11 +235,58 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
groupInfos.add(json.addOptions(options).format(
|
groupInfos.add(json.addOptions(options).format(
|
||||||
GroupDescriptions.forAccountGroup(group)));
|
GroupDescriptions.forAccountGroup(group)));
|
||||||
}
|
}
|
||||||
|
return groupInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GroupInfo> suggestGroups() throws OrmException, BadRequestException {
|
||||||
|
if (conflictingSuggestParameters()) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"You should only have no more than one --project and -n with --suggest");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GroupReference> groupRefs = Lists.newArrayList(Iterables.limit(
|
||||||
|
groupBackend.suggest(
|
||||||
|
suggest, Iterables.getFirst(projects, null)),
|
||||||
|
limit <= 0 ? 10 : Math.min(limit, 10)));
|
||||||
|
|
||||||
|
List<GroupInfo> groupInfos = Lists.newArrayListWithCapacity(groupRefs.size());
|
||||||
|
for (final GroupReference ref : groupRefs) {
|
||||||
|
GroupDescription.Basic desc = groupBackend.get(ref.getUUID());
|
||||||
|
if (desc != null) {
|
||||||
|
groupInfos.add(json.addOptions(options).format(desc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return groupInfos;
|
return groupInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean conflictingSuggestParameters() {
|
||||||
|
if (Strings.isNullOrEmpty(suggest)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (projects.size() > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (visibleToAll) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (user != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (owned) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (start != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!groupsToInspect.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(matchSubstring)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user)
|
private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user)
|
||||||
throws OrmException {
|
throws OrmException {
|
||||||
List<GroupInfo> groups = Lists.newArrayList();
|
List<GroupInfo> groups = Lists.newArrayList();
|
||||||
|
@ -19,10 +19,12 @@ import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
|
|||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.gerrit.extensions.common.GroupInfo;
|
import com.google.gerrit.extensions.common.GroupInfo;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.Url;
|
import com.google.gerrit.extensions.restapi.Url;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.GetGroups;
|
import com.google.gerrit.server.account.GetGroups;
|
||||||
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.group.GroupJson;
|
import com.google.gerrit.server.group.GroupJson;
|
||||||
@ -71,12 +73,13 @@ public class ListGroupsCommand extends SshCommand {
|
|||||||
final Provider<IdentifiedUser> identifiedUser,
|
final Provider<IdentifiedUser> identifiedUser,
|
||||||
final IdentifiedUser.GenericFactory userFactory,
|
final IdentifiedUser.GenericFactory userFactory,
|
||||||
final Provider<GetGroups> accountGetGroups,
|
final Provider<GetGroups> accountGetGroups,
|
||||||
final GroupJson json) {
|
final GroupJson json,
|
||||||
|
GroupBackend groupBackend) {
|
||||||
super(groupCache, groupControlFactory, genericGroupControlFactory,
|
super(groupCache, groupControlFactory, genericGroupControlFactory,
|
||||||
identifiedUser, userFactory, accountGetGroups, json);
|
identifiedUser, userFactory, accountGetGroups, json, groupBackend);
|
||||||
}
|
}
|
||||||
|
|
||||||
void display(final PrintWriter out) throws OrmException {
|
void display(final PrintWriter out) throws OrmException, BadRequestException {
|
||||||
final ColumnFormatter formatter = new ColumnFormatter(out, '\t');
|
final ColumnFormatter formatter = new ColumnFormatter(out, '\t');
|
||||||
for (final GroupInfo info : get()) {
|
for (final GroupInfo info : get()) {
|
||||||
formatter.addColumn(MoreObjects.firstNonNull(info.name, "n/a"));
|
formatter.addColumn(MoreObjects.firstNonNull(info.name, "n/a"));
|
||||||
|
Loading…
Reference in New Issue
Block a user