Merge "ListGroups: Add support to list groups by owner"
This commit is contained in:
@@ -124,6 +124,40 @@ client so they are generally disabled by default. Optional fields are:
|
|||||||
* `MEMBERS`: include list of direct group members.
|
* `MEMBERS`: include list of direct group members.
|
||||||
--
|
--
|
||||||
|
|
||||||
|
==== Find groups that are owned by another group
|
||||||
|
|
||||||
|
By setting `ownedBy` and specifying the link:#group-id[\{group-id\}] of another
|
||||||
|
group, it is possible to find all the groups for which the owning group is the
|
||||||
|
given group.
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
GET /groups/?ownedBy=7ca042f4d5847936fcb90ca91057673157fd06fc HTTP/1.0
|
||||||
|
----
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json; charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"MyProject-Committers": {
|
||||||
|
"id": "9999c971bb4ab872aab759d8c49833ee6b9ff320",
|
||||||
|
"url": "#/admin/groups/uuid-9999c971bb4ab872aab759d8c49833ee6b9ff320",
|
||||||
|
"options": {
|
||||||
|
"visible_to_all": true
|
||||||
|
},
|
||||||
|
"description":"contains all committers for MyProject",
|
||||||
|
"group_id": 551,
|
||||||
|
"owner": "MyProject-Owners",
|
||||||
|
"owner_id": "7ca042f4d5847936fcb90ca91057673157fd06fc",
|
||||||
|
"created_on": "2013-02-01 09:59:32.126000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
==== Check if a group is owned by the calling user
|
==== Check if a group is owned by the calling user
|
||||||
By setting the option `owned` and specifying a group to inspect with
|
By setting the option `owned` and specifying a group to inspect with
|
||||||
the option `group`/`g`, it is possible to find out if this group is
|
the option `group`/`g`, it is possible to find out if this group is
|
||||||
|
|||||||
@@ -490,6 +490,33 @@ public class GroupsIT extends AbstractDaemonTest {
|
|||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getGroupsByOwner() throws Exception {
|
||||||
|
String parent = createGroup("test-parent");
|
||||||
|
List<String> children =
|
||||||
|
Arrays.asList(createGroup("test-child1", parent), createGroup("test-child2", parent));
|
||||||
|
|
||||||
|
// By UUID
|
||||||
|
List<GroupInfo> owned =
|
||||||
|
gApi.groups().list().withOwnedBy(getFromCache(parent).getGroupUUID().get()).get();
|
||||||
|
assertThat(owned.stream().map(g -> g.name).collect(toList()))
|
||||||
|
.containsExactlyElementsIn(children);
|
||||||
|
|
||||||
|
// By name
|
||||||
|
owned = gApi.groups().list().withOwnedBy(parent).get();
|
||||||
|
assertThat(owned.stream().map(g -> g.name).collect(toList()))
|
||||||
|
.containsExactlyElementsIn(children);
|
||||||
|
|
||||||
|
// By group that does not own any others
|
||||||
|
owned = gApi.groups().list().withOwnedBy(owned.get(0).id).get();
|
||||||
|
assertThat(owned).isEmpty();
|
||||||
|
|
||||||
|
// By non-existing group
|
||||||
|
exception.expect(UnprocessableEntityException.class);
|
||||||
|
exception.expectMessage("Group Not Found: does-not-exist");
|
||||||
|
gApi.groups().list().withOwnedBy("does-not-exist").get();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onlyVisibleGroupsReturned() throws Exception {
|
public void onlyVisibleGroupsReturned() throws Exception {
|
||||||
String newGroupName = name("newGroup");
|
String newGroupName = name("newGroup");
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public interface Groups {
|
|||||||
private String substring;
|
private String substring;
|
||||||
private String suggest;
|
private String suggest;
|
||||||
private String regex;
|
private String regex;
|
||||||
|
private String ownedBy;
|
||||||
|
|
||||||
public List<GroupInfo> get() throws RestApiException {
|
public List<GroupInfo> get() throws RestApiException {
|
||||||
Map<String, GroupInfo> map = getAsMap();
|
Map<String, GroupInfo> map = getAsMap();
|
||||||
@@ -160,6 +161,11 @@ public interface Groups {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListRequest withOwnedBy(String ownedBy) {
|
||||||
|
this.ownedBy = ownedBy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public EnumSet<ListGroupsOption> getOptions() {
|
public EnumSet<ListGroupsOption> getOptions() {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
@@ -203,6 +209,10 @@ public interface Groups {
|
|||||||
public String getSuggest() {
|
public String getSuggest() {
|
||||||
return suggest;
|
return suggest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOwnedBy() {
|
||||||
|
return ownedBy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ class GroupsImpl implements Groups {
|
|||||||
|
|
||||||
list.setVisibleToAll(req.getVisibleToAll());
|
list.setVisibleToAll(req.getVisibleToAll());
|
||||||
|
|
||||||
|
if (req.getOwnedBy() != null) {
|
||||||
|
list.setOwnedBy(req.getOwnedBy());
|
||||||
|
}
|
||||||
|
|
||||||
if (req.getUser() != null) {
|
if (req.getUser() != null) {
|
||||||
try {
|
try {
|
||||||
list.setUser(accounts.parse(req.getUser()).getAccountId());
|
list.setUser(accounts.parse(req.getUser()).getAccountId());
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ 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.BadRequestException;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
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;
|
||||||
@@ -55,6 +56,7 @@ import java.util.Locale;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
@@ -76,6 +78,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
private final GroupJson json;
|
private final GroupJson json;
|
||||||
private final GroupBackend groupBackend;
|
private final GroupBackend groupBackend;
|
||||||
private final Groups groups;
|
private final Groups groups;
|
||||||
|
private final GroupsCollection groupsCollection;
|
||||||
private final Provider<ReviewDb> db;
|
private final Provider<ReviewDb> db;
|
||||||
|
|
||||||
private EnumSet<ListGroupsOption> options = EnumSet.noneOf(ListGroupsOption.class);
|
private EnumSet<ListGroupsOption> options = EnumSet.noneOf(ListGroupsOption.class);
|
||||||
@@ -87,6 +90,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
private String matchSubstring;
|
private String matchSubstring;
|
||||||
private String matchRegex;
|
private String matchRegex;
|
||||||
private String suggest;
|
private String suggest;
|
||||||
|
private String ownedBy;
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
name = "--project",
|
name = "--project",
|
||||||
@@ -208,6 +212,11 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
|
options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Option(name = "--owned-by", usage = "list groups owned by the given group uuid")
|
||||||
|
public void setOwnedBy(String ownedBy) {
|
||||||
|
this.ownedBy = ownedBy;
|
||||||
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ListGroups(
|
protected ListGroups(
|
||||||
final GroupCache groupCache,
|
final GroupCache groupCache,
|
||||||
@@ -216,6 +225,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
final Provider<IdentifiedUser> identifiedUser,
|
final Provider<IdentifiedUser> identifiedUser,
|
||||||
final IdentifiedUser.GenericFactory userFactory,
|
final IdentifiedUser.GenericFactory userFactory,
|
||||||
final GetGroups accountGetGroups,
|
final GetGroups accountGetGroups,
|
||||||
|
final GroupsCollection groupsCollection,
|
||||||
GroupJson json,
|
GroupJson json,
|
||||||
GroupBackend groupBackend,
|
GroupBackend groupBackend,
|
||||||
Groups groups,
|
Groups groups,
|
||||||
@@ -229,6 +239,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
this.json = json;
|
this.json = json;
|
||||||
this.groupBackend = groupBackend;
|
this.groupBackend = groupBackend;
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
|
this.groupsCollection = groupsCollection;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +257,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, BadRequestException {
|
throws OrmException, RestApiException {
|
||||||
SortedMap<String, GroupInfo> output = new TreeMap<>();
|
SortedMap<String, GroupInfo> output = new TreeMap<>();
|
||||||
for (GroupInfo info : get()) {
|
for (GroupInfo info : get()) {
|
||||||
output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
|
output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
|
||||||
@@ -255,7 +266,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GroupInfo> get() throws OrmException, BadRequestException {
|
public List<GroupInfo> get() throws OrmException, RestApiException {
|
||||||
if (!Strings.isNullOrEmpty(suggest)) {
|
if (!Strings.isNullOrEmpty(suggest)) {
|
||||||
return suggestGroups();
|
return suggestGroups();
|
||||||
}
|
}
|
||||||
@@ -264,6 +275,10 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
throw new BadRequestException("Specify one of m/r");
|
throw new BadRequestException("Specify one of m/r");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ownedBy != null) {
|
||||||
|
return getGroupsOwnedBy(ownedBy);
|
||||||
|
}
|
||||||
|
|
||||||
if (owned) {
|
if (owned) {
|
||||||
return getGroupsOwnedBy(user != null ? userFactory.create(user) : identifiedUser.get());
|
return getGroupsOwnedBy(user != null ? userFactory.create(user) : identifiedUser.get());
|
||||||
}
|
}
|
||||||
@@ -345,6 +360,9 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
if (owned) {
|
if (owned) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (ownedBy != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (start != 0) {
|
if (start != 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -360,14 +378,15 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) throws OrmException {
|
private List<GroupInfo> filterGroupsOwnedBy(Predicate<GroupDescription.Internal> filter)
|
||||||
|
throws OrmException {
|
||||||
Pattern pattern = getRegexPattern();
|
Pattern pattern = getRegexPattern();
|
||||||
Stream<GroupDescription.Internal> foundGroups =
|
Stream<GroupDescription.Internal> foundGroups =
|
||||||
groups
|
groups
|
||||||
.getAll(db.get())
|
.getAll(db.get())
|
||||||
.map(GroupDescriptions::forAccountGroup)
|
.map(GroupDescriptions::forAccountGroup)
|
||||||
.filter(group -> !isNotRelevant(pattern, group))
|
.filter(group -> !isNotRelevant(pattern, group))
|
||||||
.filter(group -> isOwner(user, group))
|
.filter(filter)
|
||||||
.sorted(GROUP_COMPARATOR)
|
.sorted(GROUP_COMPARATOR)
|
||||||
.skip(start);
|
.skip(start);
|
||||||
if (limit > 0) {
|
if (limit > 0) {
|
||||||
@@ -381,6 +400,15 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
return groupInfos;
|
return groupInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<GroupInfo> getGroupsOwnedBy(String id) throws OrmException, RestApiException {
|
||||||
|
String uuid = groupsCollection.parse(id).getGroupUUID().get();
|
||||||
|
return filterGroupsOwnedBy(group -> group.getOwnerGroupUUID().get().equals(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) throws OrmException {
|
||||||
|
return filterGroupsOwnedBy(group -> isOwner(user, group));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isOwner(CurrentUser user, GroupDescription.Internal group) {
|
private boolean isOwner(CurrentUser user, GroupDescription.Internal group) {
|
||||||
try {
|
try {
|
||||||
return genericGroupControlFactory.controlFor(user, group.getGroupUUID()).isOwner();
|
return genericGroupControlFactory.controlFor(user, group.getGroupUUID()).isOwner();
|
||||||
|
|||||||
Reference in New Issue
Block a user