Merge changes from topic "add-members-subgroups-to-group-index"
* changes: Add fields for members and subgroups to the group index Use the term 'subgroup' instead of 'includes' where possible Avoid unnecessary loading of members and subgroups Cache instances of type InternalGroup instead of AccountGroup by UUID Base group index on new class InternalGroup
This commit is contained in:
@@ -116,7 +116,7 @@ client so they are generally disabled by default. Optional fields are:
|
|||||||
|
|
||||||
[[includes]]
|
[[includes]]
|
||||||
--
|
--
|
||||||
* `INCLUDES`: include list of directly included groups.
|
* `INCLUDES`: include list of direct subgroups.
|
||||||
--
|
--
|
||||||
|
|
||||||
[[members]]
|
[[members]]
|
||||||
@@ -1129,13 +1129,13 @@ already a member of the group.
|
|||||||
]
|
]
|
||||||
----
|
----
|
||||||
|
|
||||||
[[delete-group-member]]
|
[[remove-group-member]]
|
||||||
=== Delete Group Member
|
=== Remove Group Member
|
||||||
--
|
--
|
||||||
'DELETE /groups/link:#group-id[\{group-id\}]/members/link:rest-api-accounts.html#account-id[\{account-id\}]'
|
'DELETE /groups/link:#group-id[\{group-id\}]/members/link:rest-api-accounts.html#account-id[\{account-id\}]'
|
||||||
--
|
--
|
||||||
|
|
||||||
Deletes a user from a Gerrit internal group.
|
Removes a user from a Gerrit internal group.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
@@ -1147,15 +1147,15 @@ Deletes a user from a Gerrit internal group.
|
|||||||
HTTP/1.1 204 No Content
|
HTTP/1.1 204 No Content
|
||||||
----
|
----
|
||||||
|
|
||||||
[[delete-group-members]]
|
[[remove-group-members]]
|
||||||
=== Delete Group Members
|
=== Remove Group Members
|
||||||
--
|
--
|
||||||
'POST /groups/link:#group-id[\{group-id\}]/members.delete'
|
'POST /groups/link:#group-id[\{group-id\}]/members.delete'
|
||||||
--
|
--
|
||||||
|
|
||||||
Delete one or several users from a Gerrit internal group.
|
Removes one or several users from a Gerrit internal group.
|
||||||
|
|
||||||
The users to be deleted from the group must be provided in the request
|
The users to be removed from the group must be provided in the request
|
||||||
body as a link:#members-input[MembersInput] entity.
|
body as a link:#members-input[MembersInput] entity.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
@@ -1176,16 +1176,16 @@ body as a link:#members-input[MembersInput] entity.
|
|||||||
HTTP/1.1 204 No Content
|
HTTP/1.1 204 No Content
|
||||||
----
|
----
|
||||||
|
|
||||||
[[group-include-endpoints]]
|
[[subgroup-endpoints]]
|
||||||
== Group Include Endpoints
|
== Subgroup Endpoints
|
||||||
|
|
||||||
[[included-groups]]
|
[[list-subgroups]]
|
||||||
=== List Included Groups
|
=== List Subgroups
|
||||||
--
|
--
|
||||||
'GET /groups/link:#group-id[\{group-id\}]/groups/'
|
'GET /groups/link:#group-id[\{group-id\}]/groups/'
|
||||||
--
|
--
|
||||||
|
|
||||||
Lists the directly included groups of a group.
|
Lists the direct subgroups of a group.
|
||||||
|
|
||||||
As result a list of link:#group-info[GroupInfo] entries is returned.
|
As result a list of link:#group-info[GroupInfo] entries is returned.
|
||||||
The entries in the list are sorted by group name and UUID.
|
The entries in the list are sorted by group name and UUID.
|
||||||
@@ -1217,13 +1217,13 @@ The entries in the list are sorted by group name and UUID.
|
|||||||
]
|
]
|
||||||
----
|
----
|
||||||
|
|
||||||
[[get-included-group]]
|
[[get-subgroup]]
|
||||||
=== Get Included Group
|
=== Get Subgroup
|
||||||
--
|
--
|
||||||
'GET /groups/link:#group-id[\{group-id\}]/groups/link:#group-id[\{group-id\}]'
|
'GET /groups/link:#group-id[\{group-id\}]/groups/link:#group-id[\{group-id\}]'
|
||||||
--
|
--
|
||||||
|
|
||||||
Retrieves an included group.
|
Retrieves a subgroup.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
@@ -1231,7 +1231,7 @@ Retrieves an included group.
|
|||||||
----
|
----
|
||||||
|
|
||||||
As response a link:#group-info[GroupInfo] entity is returned that
|
As response a link:#group-info[GroupInfo] entity is returned that
|
||||||
describes the included group.
|
describes the subgroup.
|
||||||
|
|
||||||
.Response
|
.Response
|
||||||
----
|
----
|
||||||
@@ -1253,13 +1253,13 @@ describes the included group.
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
[[include-group]]
|
[[add-subgroup]]
|
||||||
=== Include Group
|
=== Add Subgroup
|
||||||
--
|
--
|
||||||
'PUT /groups/link:#group-id[\{group-id\}]/groups/link:#group-id[\{group-id\}]'
|
'PUT /groups/link:#group-id[\{group-id\}]/groups/link:#group-id[\{group-id\}]'
|
||||||
--
|
--
|
||||||
|
|
||||||
Includes an internal or external group into a Gerrit internal group.
|
Adds an internal or external group as subgroup to a Gerrit internal group.
|
||||||
External groups must be specified using the UUID.
|
External groups must be specified using the UUID.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
@@ -1268,7 +1268,7 @@ External groups must be specified using the UUID.
|
|||||||
----
|
----
|
||||||
|
|
||||||
As response a link:#group-info[GroupInfo] entity is returned that
|
As response a link:#group-info[GroupInfo] entity is returned that
|
||||||
describes the included group.
|
describes the subgroup.
|
||||||
|
|
||||||
.Response
|
.Response
|
||||||
----
|
----
|
||||||
@@ -1290,11 +1290,11 @@ describes the included group.
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The request also succeeds if the group is already included in this
|
The request also succeeds if the group is already a subgroup of this
|
||||||
group, but then the HTTP response code is `200 OK`.
|
group.
|
||||||
|
|
||||||
[[include-groups]]
|
[[add-subgroups]]
|
||||||
=== Include Groups
|
=== Add Subgroups
|
||||||
--
|
--
|
||||||
'POST /groups/link:#group-id[\{group-id\}]/groups'
|
'POST /groups/link:#group-id[\{group-id\}]/groups'
|
||||||
--
|
--
|
||||||
@@ -1305,10 +1305,10 @@ OR
|
|||||||
'POST /groups/link:#group-id[\{group-id\}]/groups.add'
|
'POST /groups/link:#group-id[\{group-id\}]/groups.add'
|
||||||
--
|
--
|
||||||
|
|
||||||
Includes one or several groups into a Gerrit internal group.
|
Adds one or several groups as subgroups to a Gerrit internal group.
|
||||||
|
|
||||||
The groups to be included into the group must be provided in the
|
The subgroups to be added must be provided in the request body as a
|
||||||
request body as a link:#groups-input[GroupsInput] entity.
|
link:#groups-input[GroupsInput] entity.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
@@ -1327,8 +1327,8 @@ As response a list of link:#group-info[GroupInfo] entities is
|
|||||||
returned that describes the groups that were specified in the
|
returned that describes the groups that were specified in the
|
||||||
link:#groups-input[GroupsInput]. A link:#group-info[GroupInfo] entity
|
link:#groups-input[GroupsInput]. A link:#group-info[GroupInfo] entity
|
||||||
is returned for each group specified in the input, independently of
|
is returned for each group specified in the input, independently of
|
||||||
whether the group was newly included into the group or whether the
|
whether the group was newly added as subgroup or whether the
|
||||||
group was already included in the group.
|
group was already a subgroup of the group.
|
||||||
|
|
||||||
.Response
|
.Response
|
||||||
----
|
----
|
||||||
@@ -1363,13 +1363,13 @@ group was already included in the group.
|
|||||||
]
|
]
|
||||||
----
|
----
|
||||||
|
|
||||||
[[delete-included-group]]
|
[[remove-subgroup]]
|
||||||
=== Delete Included Group
|
=== Remove Subgroup
|
||||||
--
|
--
|
||||||
'DELETE /groups/link:#group-id[\{group-id\}]/groups/link:#group-id[\{group-id\}]'
|
'DELETE /groups/link:#group-id[\{group-id\}]/groups/link:#group-id[\{group-id\}]'
|
||||||
--
|
--
|
||||||
|
|
||||||
Deletes an included group from a Gerrit internal group.
|
Removes a subgroup from a Gerrit internal group.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
@@ -1381,16 +1381,16 @@ Deletes an included group from a Gerrit internal group.
|
|||||||
HTTP/1.1 204 No Content
|
HTTP/1.1 204 No Content
|
||||||
----
|
----
|
||||||
|
|
||||||
[[delete-included-groups]]
|
[[remove-subgroups]]
|
||||||
=== Delete Included Groups
|
=== Remove Subgroups
|
||||||
--
|
--
|
||||||
'POST /groups/link:#group-id[\{group-id\}]/groups.delete'
|
'POST /groups/link:#group-id[\{group-id\}]/groups.delete'
|
||||||
--
|
--
|
||||||
|
|
||||||
Delete one or several included groups from a Gerrit internal group.
|
Removes one or several subgroups from a Gerrit internal group.
|
||||||
|
|
||||||
The groups to be deleted from the group must be provided in the request
|
The subgroups to be removed must be provided in the request body as a
|
||||||
body as a link:#groups-input[GroupsInput] entity.
|
link:#groups-input[GroupsInput] entity.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
@@ -1495,9 +1495,9 @@ A list of link:rest-api-accounts.html#account-info[AccountInfo]
|
|||||||
entities describing the direct members. +
|
entities describing the direct members. +
|
||||||
Only set if link:#members[members] are requested.
|
Only set if link:#members[members] are requested.
|
||||||
|`includes` |optional, only for internal groups|
|
|`includes` |optional, only for internal groups|
|
||||||
A list of link:#group-info[GroupInfo] entities describing the directly
|
A list of link:#group-info[GroupInfo] entities describing the direct
|
||||||
included groups. +
|
subgroups. +
|
||||||
Only set if link:#includes[included groups] are requested.
|
Only set if link:#includes[subgroups] are requested.
|
||||||
|===========================
|
|===========================
|
||||||
|
|
||||||
The type of a group can be deduced from the group's UUID:
|
The type of a group can be deduced from the group's UUID:
|
||||||
|
|||||||
@@ -59,6 +59,17 @@ uuid:'UUID'::
|
|||||||
+
|
+
|
||||||
Matches groups that have the UUID 'UUID'.
|
Matches groups that have the UUID 'UUID'.
|
||||||
|
|
||||||
|
[[member]]
|
||||||
|
member:'MEMBER'::
|
||||||
|
+
|
||||||
|
Matches groups that have the account represented by 'MEMBER' as a member.
|
||||||
|
|
||||||
|
[[subgroup]]
|
||||||
|
subgroup:'SUBGROUP'::
|
||||||
|
+
|
||||||
|
Matches groups that have a subgroup whose name best matches 'SUBGROUP' or
|
||||||
|
whose UUID is 'SUBGROUP'.
|
||||||
|
|
||||||
== Magical Operators
|
== Magical Operators
|
||||||
|
|
||||||
[[is-visible]]
|
[[is-visible]]
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import com.google.gerrit.server.config.PluginConfigFactory;
|
|||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
import com.google.gerrit.server.git.ProjectConfig;
|
import com.google.gerrit.server.git.ProjectConfig;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.group.SystemGroupBackend;
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||||
import com.google.gerrit.server.index.change.ChangeIndex;
|
import com.google.gerrit.server.index.change.ChangeIndex;
|
||||||
import com.google.gerrit.server.index.change.ChangeIndexCollection;
|
import com.google.gerrit.server.index.change.ChangeIndexCollection;
|
||||||
@@ -352,7 +353,7 @@ public abstract class AbstractDaemonTest {
|
|||||||
// removes all indexed data.
|
// removes all indexed data.
|
||||||
// As a workaround, we simply reindex all available groups here.
|
// As a workaround, we simply reindex all available groups here.
|
||||||
for (AccountGroup group : groupCache.all()) {
|
for (AccountGroup group : groupCache.all()) {
|
||||||
groupCache.evict(group);
|
groupCache.evict(group.getGroupUUID(), group.getId(), group.getNameKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
admin = accountCreator.admin();
|
admin = accountCreator.admin();
|
||||||
@@ -1178,8 +1179,9 @@ public abstract class AbstractDaemonTest {
|
|||||||
String g = createGroup("cla-test-group");
|
String g = createGroup("cla-test-group");
|
||||||
GroupApi groupApi = gApi.groups().id(g);
|
GroupApi groupApi = gApi.groups().id(g);
|
||||||
groupApi.description("CLA test group");
|
groupApi.description("CLA test group");
|
||||||
AccountGroup caGroup = groupCache.get(new AccountGroup.UUID(groupApi.detail().id));
|
InternalGroup caGroup =
|
||||||
GroupReference groupRef = GroupReference.forGroup(caGroup);
|
groupCache.get(new AccountGroup.UUID(groupApi.detail().id)).orElse(null);
|
||||||
|
GroupReference groupRef = new GroupReference(caGroup.getGroupUUID(), caGroup.getName());
|
||||||
PermissionRule rule = new PermissionRule(groupRef);
|
PermissionRule rule = new PermissionRule(groupRef);
|
||||||
rule.setAction(PermissionRule.Action.ALLOW);
|
rule.setAction(PermissionRule.Action.ALLOW);
|
||||||
ca = new ContributorAgreement("cla-test");
|
ca = new ContributorAgreement("cla-test");
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.google.gerrit.extensions.restapi.TopLevelResource;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.group.CreateGroup;
|
import com.google.gerrit.server.group.CreateGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -44,9 +45,9 @@ import org.junit.Test;
|
|||||||
public class SuggestReviewersIT extends AbstractDaemonTest {
|
public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||||
@Inject private CreateGroup.Factory createGroupFactory;
|
@Inject private CreateGroup.Factory createGroupFactory;
|
||||||
|
|
||||||
private AccountGroup group1;
|
private InternalGroup group1;
|
||||||
private AccountGroup group2;
|
private InternalGroup group2;
|
||||||
private AccountGroup group3;
|
private InternalGroup group3;
|
||||||
|
|
||||||
private TestAccount user1;
|
private TestAccount user1;
|
||||||
private TestAccount user2;
|
private TestAccount user2;
|
||||||
@@ -234,8 +235,8 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
|||||||
@GerritConfig(name = "addreviewer.maxAllowed", value = "2")
|
@GerritConfig(name = "addreviewer.maxAllowed", value = "2")
|
||||||
@GerritConfig(name = "addreviewer.maxWithoutConfirmation", value = "1")
|
@GerritConfig(name = "addreviewer.maxWithoutConfirmation", value = "1")
|
||||||
public void suggestReviewersGroupSizeConsiderations() throws Exception {
|
public void suggestReviewersGroupSizeConsiderations() throws Exception {
|
||||||
AccountGroup largeGroup = group("large");
|
InternalGroup largeGroup = group("large");
|
||||||
AccountGroup mediumGroup = group("medium");
|
InternalGroup mediumGroup = group("medium");
|
||||||
|
|
||||||
// Both groups have Administrator as a member. Add two users to large
|
// Both groups have Administrator as a member. Add two users to large
|
||||||
// group to push it past maxAllowed, and one to medium group to push it
|
// group to push it past maxAllowed, and one to medium group to push it
|
||||||
@@ -424,19 +425,19 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
|||||||
return gApi.changes().id(changeId).suggestReviewers(query).withLimit(n).get();
|
return gApi.changes().id(changeId).suggestReviewers(query).withLimit(n).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountGroup group(String name) throws Exception {
|
private InternalGroup group(String name) throws Exception {
|
||||||
GroupInfo group = createGroupFactory.create(name(name)).apply(TopLevelResource.INSTANCE, null);
|
GroupInfo group = createGroupFactory.create(name(name)).apply(TopLevelResource.INSTANCE, null);
|
||||||
return groupCache.get(new AccountGroup.UUID(group.id));
|
return groupCache.get(new AccountGroup.UUID(group.id)).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestAccount user(String name, String fullName, String emailName, AccountGroup... groups)
|
private TestAccount user(String name, String fullName, String emailName, InternalGroup... groups)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String[] groupNames = Arrays.stream(groups).map(AccountGroup::getName).toArray(String[]::new);
|
String[] groupNames = Arrays.stream(groups).map(InternalGroup::getName).toArray(String[]::new);
|
||||||
return accountCreator.create(
|
return accountCreator.create(
|
||||||
name(name), name(emailName) + "@example.com", fullName, groupNames);
|
name(name), name(emailName) + "@example.com", fullName, groupNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestAccount user(String name, String fullName, AccountGroup... groups) throws Exception {
|
private TestAccount user(String name, String fullName, InternalGroup... groups) throws Exception {
|
||||||
return user(name, fullName, name, groups);
|
return user(name, fullName, name, groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +462,7 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
|||||||
private void assertReviewers(
|
private void assertReviewers(
|
||||||
List<SuggestedReviewerInfo> actual,
|
List<SuggestedReviewerInfo> actual,
|
||||||
List<TestAccount> expectedUsers,
|
List<TestAccount> expectedUsers,
|
||||||
List<AccountGroup> expectedGroups) {
|
List<InternalGroup> expectedGroups) {
|
||||||
List<Integer> actualAccountIds =
|
List<Integer> actualAccountIds =
|
||||||
actual
|
actual
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public class GroupDescription {
|
|||||||
|
|
||||||
AccountGroup.Id getId();
|
AccountGroup.Id getId();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
String getDescription();
|
String getDescription();
|
||||||
|
|
||||||
AccountGroup.UUID getOwnerGroupUUID();
|
AccountGroup.UUID getOwnerGroupUUID();
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public class GroupDescriptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return group.getDescription();
|
return group.getDescription();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
// Copyright (C) 2008 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.common.data;
|
|
||||||
|
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GroupDetail {
|
|
||||||
private Set<Account.Id> members;
|
|
||||||
private Set<AccountGroup.UUID> includes;
|
|
||||||
|
|
||||||
public GroupDetail(Set<Account.Id> members, Set<AccountGroup.UUID> includes) {
|
|
||||||
this.members = members;
|
|
||||||
this.includes = includes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Account.Id> getMembers() {
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<AccountGroup.UUID> getIncludes() {
|
|
||||||
return includes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,6 +27,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
|||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.gerrit.server.index.group.GroupField;
|
import com.google.gerrit.server.index.group.GroupField;
|
||||||
import com.google.gerrit.server.index.group.GroupIndex;
|
import com.google.gerrit.server.index.group.GroupIndex;
|
||||||
@@ -48,6 +49,7 @@ import java.io.IOException;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
@@ -55,12 +57,12 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, AccountGroup>
|
public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, InternalGroup>
|
||||||
implements GroupIndex {
|
implements GroupIndex {
|
||||||
static class GroupMapping {
|
static class GroupMapping {
|
||||||
MappingProperties groups;
|
MappingProperties groups;
|
||||||
|
|
||||||
GroupMapping(Schema<AccountGroup> schema) {
|
GroupMapping(Schema<InternalGroup> schema) {
|
||||||
this.groups = ElasticMapping.createMapping(schema);
|
this.groups = ElasticMapping.createMapping(schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,14 +81,14 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, A
|
|||||||
SitePaths sitePaths,
|
SitePaths sitePaths,
|
||||||
Provider<GroupCache> groupCache,
|
Provider<GroupCache> groupCache,
|
||||||
JestClientBuilder clientBuilder,
|
JestClientBuilder clientBuilder,
|
||||||
@Assisted Schema<AccountGroup> schema) {
|
@Assisted Schema<InternalGroup> schema) {
|
||||||
super(cfg, sitePaths, schema, clientBuilder, GROUPS_PREFIX);
|
super(cfg, sitePaths, schema, clientBuilder, GROUPS_PREFIX);
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.mapping = new GroupMapping(schema);
|
this.mapping = new GroupMapping(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replace(AccountGroup group) throws IOException {
|
public void replace(InternalGroup group) throws IOException {
|
||||||
Bulk bulk =
|
Bulk bulk =
|
||||||
new Bulk.Builder()
|
new Bulk.Builder()
|
||||||
.defaultIndex(indexName)
|
.defaultIndex(indexName)
|
||||||
@@ -104,7 +106,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, A
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataSource<AccountGroup> getSource(Predicate<AccountGroup> p, QueryOptions opts)
|
public DataSource<InternalGroup> getSource(Predicate<InternalGroup> p, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new QuerySource(p, opts);
|
return new QuerySource(p, opts);
|
||||||
}
|
}
|
||||||
@@ -121,15 +123,15 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, A
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getId(AccountGroup group) {
|
protected String getId(InternalGroup group) {
|
||||||
return group.getGroupUUID().get();
|
return group.getGroupUUID().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySource implements DataSource<AccountGroup> {
|
private class QuerySource implements DataSource<InternalGroup> {
|
||||||
private final Search search;
|
private final Search search;
|
||||||
private final Set<String> fields;
|
private final Set<String> fields;
|
||||||
|
|
||||||
QuerySource(Predicate<AccountGroup> p, QueryOptions opts) throws QueryParseException {
|
QuerySource(Predicate<InternalGroup> p, QueryOptions opts) throws QueryParseException {
|
||||||
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
||||||
fields = IndexUtils.groupFields(opts);
|
fields = IndexUtils.groupFields(opts);
|
||||||
SearchSourceBuilder searchSource =
|
SearchSourceBuilder searchSource =
|
||||||
@@ -156,9 +158,9 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, A
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResultSet<AccountGroup> read() throws OrmException {
|
public ResultSet<InternalGroup> read() throws OrmException {
|
||||||
try {
|
try {
|
||||||
List<AccountGroup> results = Collections.emptyList();
|
List<InternalGroup> results = Collections.emptyList();
|
||||||
JestResult result = client.execute(search);
|
JestResult result = client.execute(search);
|
||||||
if (result.isSucceeded()) {
|
if (result.isSucceeded()) {
|
||||||
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
||||||
@@ -166,21 +168,22 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, A
|
|||||||
JsonArray json = obj.getAsJsonArray("hits");
|
JsonArray json = obj.getAsJsonArray("hits");
|
||||||
results = Lists.newArrayListWithCapacity(json.size());
|
results = Lists.newArrayListWithCapacity(json.size());
|
||||||
for (int i = 0; i < json.size(); i++) {
|
for (int i = 0; i < json.size(); i++) {
|
||||||
results.add(toAccountGroup(json.get(i)));
|
Optional<InternalGroup> internalGroup = toInternalGroup(json.get(i));
|
||||||
|
internalGroup.ifPresent(results::add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error(result.getErrorMessage());
|
log.error(result.getErrorMessage());
|
||||||
}
|
}
|
||||||
final List<AccountGroup> r = Collections.unmodifiableList(results);
|
final List<InternalGroup> r = Collections.unmodifiableList(results);
|
||||||
return new ResultSet<AccountGroup>() {
|
return new ResultSet<InternalGroup>() {
|
||||||
@Override
|
@Override
|
||||||
public Iterator<AccountGroup> iterator() {
|
public Iterator<InternalGroup> iterator() {
|
||||||
return r.iterator();
|
return r.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AccountGroup> toList() {
|
public List<InternalGroup> toList() {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +202,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, A
|
|||||||
return search.toString();
|
return search.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountGroup toAccountGroup(JsonElement json) {
|
private Optional<InternalGroup> toInternalGroup(JsonElement json) {
|
||||||
JsonElement source = json.getAsJsonObject().get("_source");
|
JsonElement source = json.getAsJsonObject().get("_source");
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
source = json.getAsJsonObject().get("fields");
|
source = json.getAsJsonObject().get("fields");
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import com.google.gerrit.elasticsearch.ElasticAccountIndex.AccountMapping;
|
|||||||
import com.google.gerrit.elasticsearch.ElasticChangeIndex.ChangeMapping;
|
import com.google.gerrit.elasticsearch.ElasticChangeIndex.ChangeMapping;
|
||||||
import com.google.gerrit.elasticsearch.ElasticGroupIndex.GroupMapping;
|
import com.google.gerrit.elasticsearch.ElasticGroupIndex.GroupMapping;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.IndexModule.IndexType;
|
import com.google.gerrit.server.index.IndexModule.IndexType;
|
||||||
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
|
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
|
||||||
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
|
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
|
||||||
@@ -146,7 +146,7 @@ final class ElasticTestUtils {
|
|||||||
.execute()
|
.execute()
|
||||||
.actionGet();
|
.actionGet();
|
||||||
|
|
||||||
Schema<AccountGroup> groupSchema = GroupSchemaDefinitions.INSTANCE.getLatest();
|
Schema<InternalGroup> groupSchema = GroupSchemaDefinitions.INSTANCE.getLatest();
|
||||||
GroupMapping groupMapping = new GroupMapping(groupSchema);
|
GroupMapping groupMapping = new GroupMapping(groupSchema);
|
||||||
nodeInfo
|
nodeInfo
|
||||||
.node
|
.node
|
||||||
|
|||||||
@@ -109,15 +109,15 @@ public interface GroupApi {
|
|||||||
void removeMembers(String... members) throws RestApiException;
|
void removeMembers(String... members) throws RestApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List included groups.
|
* Lists the subgroups of this group.
|
||||||
*
|
*
|
||||||
* @return included groups.
|
* @return the found subgroups
|
||||||
* @throws RestApiException
|
* @throws RestApiException
|
||||||
*/
|
*/
|
||||||
List<GroupInfo> includedGroups() throws RestApiException;
|
List<GroupInfo> includedGroups() throws RestApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add groups to be included in this one.
|
* Adds subgroups to this group.
|
||||||
*
|
*
|
||||||
* @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
|
* @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
|
||||||
* @throws RestApiException
|
* @throws RestApiException
|
||||||
@@ -125,7 +125,7 @@ public interface GroupApi {
|
|||||||
void addGroups(String... groups) throws RestApiException;
|
void addGroups(String... groups) throws RestApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove included groups from this one.
|
* Removes subgroups from this group.
|
||||||
*
|
*
|
||||||
* @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
|
* @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
|
||||||
* @throws RestApiException
|
* @throws RestApiException
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
|||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.gerrit.server.index.group.GroupIndex;
|
import com.google.gerrit.server.index.group.GroupIndex;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
@@ -38,6 +39,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import org.apache.lucene.document.Document;
|
import org.apache.lucene.document.Document;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
@@ -55,7 +57,7 @@ import org.eclipse.jgit.lib.Config;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, AccountGroup>
|
public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, InternalGroup>
|
||||||
implements GroupIndex {
|
implements GroupIndex {
|
||||||
private static final Logger log = LoggerFactory.getLogger(LuceneGroupIndex.class);
|
private static final Logger log = LoggerFactory.getLogger(LuceneGroupIndex.class);
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
|
|
||||||
private static final String UUID_SORT_FIELD = sortFieldName(UUID);
|
private static final String UUID_SORT_FIELD = sortFieldName(UUID);
|
||||||
|
|
||||||
private static Term idTerm(AccountGroup group) {
|
private static Term idTerm(InternalGroup group) {
|
||||||
return idTerm(group.getGroupUUID());
|
return idTerm(group.getGroupUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,10 +74,10 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final GerritIndexWriterConfig indexWriterConfig;
|
private final GerritIndexWriterConfig indexWriterConfig;
|
||||||
private final QueryBuilder<AccountGroup> queryBuilder;
|
private final QueryBuilder<InternalGroup> queryBuilder;
|
||||||
private final Provider<GroupCache> groupCache;
|
private final Provider<GroupCache> groupCache;
|
||||||
|
|
||||||
private static Directory dir(Schema<AccountGroup> schema, Config cfg, SitePaths sitePaths)
|
private static Directory dir(Schema<?> schema, Config cfg, SitePaths sitePaths)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (LuceneIndexModule.isInMemoryTest(cfg)) {
|
if (LuceneIndexModule.isInMemoryTest(cfg)) {
|
||||||
return new RAMDirectory();
|
return new RAMDirectory();
|
||||||
@@ -89,7 +91,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
@GerritServerConfig Config cfg,
|
@GerritServerConfig Config cfg,
|
||||||
SitePaths sitePaths,
|
SitePaths sitePaths,
|
||||||
Provider<GroupCache> groupCache,
|
Provider<GroupCache> groupCache,
|
||||||
@Assisted Schema<AccountGroup> schema)
|
@Assisted Schema<InternalGroup> schema)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
super(
|
super(
|
||||||
schema,
|
schema,
|
||||||
@@ -106,7 +108,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replace(AccountGroup group) throws IOException {
|
public void replace(InternalGroup group) throws IOException {
|
||||||
try {
|
try {
|
||||||
replace(idTerm(group), toDocument(group)).get();
|
replace(idTerm(group), toDocument(group)).get();
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
@@ -124,7 +126,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataSource<AccountGroup> getSource(Predicate<AccountGroup> p, QueryOptions opts)
|
public DataSource<InternalGroup> getSource(Predicate<InternalGroup> p, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new QuerySource(
|
return new QuerySource(
|
||||||
opts,
|
opts,
|
||||||
@@ -132,7 +134,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
new Sort(new SortField(UUID_SORT_FIELD, SortField.Type.STRING, false)));
|
new Sort(new SortField(UUID_SORT_FIELD, SortField.Type.STRING, false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySource implements DataSource<AccountGroup> {
|
private class QuerySource implements DataSource<InternalGroup> {
|
||||||
private final QueryOptions opts;
|
private final QueryOptions opts;
|
||||||
private final Query query;
|
private final Query query;
|
||||||
private final Sort sort;
|
private final Sort sort;
|
||||||
@@ -149,27 +151,28 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResultSet<AccountGroup> read() throws OrmException {
|
public ResultSet<InternalGroup> read() throws OrmException {
|
||||||
IndexSearcher searcher = null;
|
IndexSearcher searcher = null;
|
||||||
try {
|
try {
|
||||||
searcher = acquire();
|
searcher = acquire();
|
||||||
int realLimit = opts.start() + opts.limit();
|
int realLimit = opts.start() + opts.limit();
|
||||||
TopFieldDocs docs = searcher.search(query, realLimit, sort);
|
TopFieldDocs docs = searcher.search(query, realLimit, sort);
|
||||||
List<AccountGroup> result = new ArrayList<>(docs.scoreDocs.length);
|
List<InternalGroup> result = new ArrayList<>(docs.scoreDocs.length);
|
||||||
for (int i = opts.start(); i < docs.scoreDocs.length; i++) {
|
for (int i = opts.start(); i < docs.scoreDocs.length; i++) {
|
||||||
ScoreDoc sd = docs.scoreDocs[i];
|
ScoreDoc sd = docs.scoreDocs[i];
|
||||||
Document doc = searcher.doc(sd.doc, IndexUtils.groupFields(opts));
|
Document doc = searcher.doc(sd.doc, IndexUtils.groupFields(opts));
|
||||||
result.add(toAccountGroup(doc));
|
Optional<InternalGroup> internalGroup = toInternalGroup(doc);
|
||||||
|
internalGroup.ifPresent(result::add);
|
||||||
}
|
}
|
||||||
final List<AccountGroup> r = Collections.unmodifiableList(result);
|
final List<InternalGroup> r = Collections.unmodifiableList(result);
|
||||||
return new ResultSet<AccountGroup>() {
|
return new ResultSet<InternalGroup>() {
|
||||||
@Override
|
@Override
|
||||||
public Iterator<AccountGroup> iterator() {
|
public Iterator<InternalGroup> iterator() {
|
||||||
return r.iterator();
|
return r.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AccountGroup> toList() {
|
public List<InternalGroup> toList() {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +195,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Acc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountGroup toAccountGroup(Document doc) {
|
private Optional<InternalGroup> toInternalGroup(Document doc) {
|
||||||
AccountGroup.UUID uuid = new AccountGroup.UUID(doc.getField(UUID.getName()).stringValue());
|
AccountGroup.UUID uuid = new AccountGroup.UUID(doc.getField(UUID.getName()).stringValue());
|
||||||
// Use the GroupCache rather than depending on any stored fields in the
|
// Use the GroupCache rather than depending on any stored fields in the
|
||||||
// document (of which there shouldn't be any).
|
// document (of which there shouldn't be any).
|
||||||
|
|||||||
@@ -15,9 +15,10 @@
|
|||||||
package com.google.gerrit.server.account;
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.gerrit.common.Nullable;
|
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/** Tracks group objects in memory for efficient access. */
|
/** Tracks group objects in memory for efficient access. */
|
||||||
public interface GroupCache {
|
public interface GroupCache {
|
||||||
@@ -26,11 +27,13 @@ public interface GroupCache {
|
|||||||
AccountGroup get(AccountGroup.NameKey name);
|
AccountGroup get(AccountGroup.NameKey name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a group definition by its UUID. The returned definition may be null if the group has
|
* Looks up an internal group by its UUID.
|
||||||
* been deleted and the UUID reference is stale, or was copied from another server.
|
*
|
||||||
|
* @param groupUuid the UUID of the internal group
|
||||||
|
* @return an {@code Optional} of the internal group, or an empty {@code Optional} if no internal
|
||||||
|
* group with this UUID exists on this server or an error occurred during lookup
|
||||||
*/
|
*/
|
||||||
@Nullable
|
Optional<InternalGroup> get(AccountGroup.UUID groupUuid);
|
||||||
AccountGroup get(AccountGroup.UUID uuid);
|
|
||||||
|
|
||||||
/** @return sorted list of groups. */
|
/** @return sorted list of groups. */
|
||||||
ImmutableList<AccountGroup> all();
|
ImmutableList<AccountGroup> all();
|
||||||
@@ -38,7 +41,8 @@ public interface GroupCache {
|
|||||||
/** Notify the cache that a new group was constructed. */
|
/** Notify the cache that a new group was constructed. */
|
||||||
void onCreateGroup(AccountGroup.NameKey newGroupName) throws IOException;
|
void onCreateGroup(AccountGroup.NameKey newGroupName) throws IOException;
|
||||||
|
|
||||||
void evict(AccountGroup group) throws IOException;
|
void evict(AccountGroup.UUID groupUuid, AccountGroup.Id groupId, AccountGroup.NameKey groupName)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
void evictAfterRename(AccountGroup.NameKey oldName, AccountGroup.NameKey newName)
|
void evictAfterRename(AccountGroup.NameKey oldName, AccountGroup.NameKey newName)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|||||||
@@ -15,15 +15,19 @@
|
|||||||
package com.google.gerrit.server.account;
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
|
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
import com.google.gerrit.common.TimeUtil;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.cache.CacheModule;
|
import com.google.gerrit.server.cache.CacheModule;
|
||||||
import com.google.gerrit.server.group.Groups;
|
import com.google.gerrit.server.group.Groups;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.group.GroupIndexer;
|
import com.google.gerrit.server.index.group.GroupIndexer;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
@@ -58,7 +62,7 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
cache(BYNAME_NAME, String.class, new TypeLiteral<Optional<AccountGroup>>() {})
|
cache(BYNAME_NAME, String.class, new TypeLiteral<Optional<AccountGroup>>() {})
|
||||||
.loader(ByNameLoader.class);
|
.loader(ByNameLoader.class);
|
||||||
|
|
||||||
cache(BYUUID_NAME, String.class, new TypeLiteral<Optional<AccountGroup>>() {})
|
cache(BYUUID_NAME, String.class, new TypeLiteral<Optional<InternalGroup>>() {})
|
||||||
.loader(ByUUIDLoader.class);
|
.loader(ByUUIDLoader.class);
|
||||||
|
|
||||||
bind(GroupCacheImpl.class);
|
bind(GroupCacheImpl.class);
|
||||||
@@ -69,7 +73,7 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
|
|
||||||
private final LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId;
|
private final LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId;
|
||||||
private final LoadingCache<String, Optional<AccountGroup>> byName;
|
private final LoadingCache<String, Optional<AccountGroup>> byName;
|
||||||
private final LoadingCache<String, Optional<AccountGroup>> byUUID;
|
private final LoadingCache<String, Optional<InternalGroup>> byUUID;
|
||||||
private final SchemaFactory<ReviewDb> schema;
|
private final SchemaFactory<ReviewDb> schema;
|
||||||
private final Provider<GroupIndexer> indexer;
|
private final Provider<GroupIndexer> indexer;
|
||||||
private final Groups groups;
|
private final Groups groups;
|
||||||
@@ -78,7 +82,7 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
GroupCacheImpl(
|
GroupCacheImpl(
|
||||||
@Named(BYID_NAME) LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId,
|
@Named(BYID_NAME) LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId,
|
||||||
@Named(BYNAME_NAME) LoadingCache<String, Optional<AccountGroup>> byName,
|
@Named(BYNAME_NAME) LoadingCache<String, Optional<AccountGroup>> byName,
|
||||||
@Named(BYUUID_NAME) LoadingCache<String, Optional<AccountGroup>> byUUID,
|
@Named(BYUUID_NAME) LoadingCache<String, Optional<InternalGroup>> byUUID,
|
||||||
SchemaFactory<ReviewDb> schema,
|
SchemaFactory<ReviewDb> schema,
|
||||||
Provider<GroupIndexer> indexer,
|
Provider<GroupIndexer> indexer,
|
||||||
Groups groups) {
|
Groups groups) {
|
||||||
@@ -102,17 +106,19 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void evict(AccountGroup group) throws IOException {
|
public void evict(
|
||||||
if (group.getId() != null) {
|
AccountGroup.UUID groupUuid, AccountGroup.Id groupId, AccountGroup.NameKey groupName)
|
||||||
byId.invalidate(group.getId());
|
throws IOException {
|
||||||
|
if (groupId != null) {
|
||||||
|
byId.invalidate(groupId);
|
||||||
}
|
}
|
||||||
if (group.getNameKey() != null) {
|
if (groupName != null) {
|
||||||
byName.invalidate(group.getNameKey().get());
|
byName.invalidate(groupName.get());
|
||||||
}
|
}
|
||||||
if (group.getGroupUUID() != null) {
|
if (groupUuid != null) {
|
||||||
byUUID.invalidate(group.getGroupUUID().get());
|
byUUID.invalidate(groupUuid.get());
|
||||||
}
|
}
|
||||||
indexer.get().index(group.getGroupUUID());
|
indexer.get().index(groupUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -135,21 +141,22 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
try {
|
try {
|
||||||
return byName.get(name.get()).orElse(null);
|
return byName.get(name.get()).orElse(null);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
log.warn(String.format("Cannot lookup group %s by name", name.get()), e);
|
log.warn(String.format("Cannot look up group %s by name", name.get()), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccountGroup get(AccountGroup.UUID uuid) {
|
public Optional<InternalGroup> get(AccountGroup.UUID groupUuid) {
|
||||||
if (uuid == null) {
|
if (groupUuid == null) {
|
||||||
return null;
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return byUUID.get(uuid.get()).orElse(null);
|
return byUUID.get(groupUuid.get());
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
log.warn(String.format("Cannot lookup group %s by name", uuid.get()), e);
|
log.warn(String.format("Cannot look up group %s by uuid", groupUuid.get()), e);
|
||||||
return null;
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +217,7 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ByUUIDLoader extends CacheLoader<String, Optional<AccountGroup>> {
|
static class ByUUIDLoader extends CacheLoader<String, Optional<InternalGroup>> {
|
||||||
private final SchemaFactory<ReviewDb> schema;
|
private final SchemaFactory<ReviewDb> schema;
|
||||||
private final Groups groups;
|
private final Groups groups;
|
||||||
|
|
||||||
@@ -221,9 +228,20 @@ public class GroupCacheImpl implements GroupCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<AccountGroup> load(String uuid) throws Exception {
|
public Optional<InternalGroup> load(String uuid) throws Exception {
|
||||||
try (ReviewDb db = schema.open()) {
|
try (ReviewDb db = schema.open()) {
|
||||||
return groups.getGroup(db, new AccountGroup.UUID(uuid));
|
AccountGroup.UUID groupUuid = new AccountGroup.UUID(uuid);
|
||||||
|
Optional<AccountGroup> accountGroup = groups.getGroup(db, groupUuid);
|
||||||
|
|
||||||
|
if (!accountGroup.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<Account.Id> members =
|
||||||
|
groups.getMembers(db, groupUuid).collect(toImmutableSet());
|
||||||
|
ImmutableSet<AccountGroup.UUID> subgroups =
|
||||||
|
groups.getSubgroups(db, groupUuid).collect(toImmutableSet());
|
||||||
|
return accountGroup.map(group -> InternalGroup.create(group, members, subgroups));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
// Copyright (C) 2011 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.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
public class GroupComparator implements Comparator<AccountGroup> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(AccountGroup group1, AccountGroup group2) {
|
|
||||||
return group1.getName().compareTo(group2.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
// Copyright (C) 2009 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 static com.google.common.collect.ImmutableSet.toImmutableSet;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.gerrit.common.data.GroupDetail;
|
|
||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
|
||||||
import com.google.gerrit.server.group.Groups;
|
|
||||||
import com.google.gwtorm.server.OrmException;
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.assistedinject.Assisted;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
public class GroupDetailFactory implements Callable<GroupDetail> {
|
|
||||||
public interface Factory {
|
|
||||||
GroupDetailFactory create(AccountGroup.UUID groupUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ReviewDb db;
|
|
||||||
private final GroupControl.Factory groupControl;
|
|
||||||
private final Groups groups;
|
|
||||||
private final GroupIncludeCache groupIncludeCache;
|
|
||||||
|
|
||||||
private final AccountGroup.UUID groupUuid;
|
|
||||||
private GroupControl control;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
GroupDetailFactory(
|
|
||||||
ReviewDb db,
|
|
||||||
GroupControl.Factory groupControl,
|
|
||||||
Groups groups,
|
|
||||||
GroupIncludeCache groupIncludeCache,
|
|
||||||
@Assisted AccountGroup.UUID groupUuid) {
|
|
||||||
this.db = db;
|
|
||||||
this.groupControl = groupControl;
|
|
||||||
this.groups = groups;
|
|
||||||
this.groupIncludeCache = groupIncludeCache;
|
|
||||||
|
|
||||||
this.groupUuid = groupUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GroupDetail call() throws OrmException, NoSuchGroupException {
|
|
||||||
control = groupControl.validateFor(groupUuid);
|
|
||||||
ImmutableSet<Account.Id> members = loadMembers();
|
|
||||||
ImmutableSet<AccountGroup.UUID> includes = loadIncludes();
|
|
||||||
return new GroupDetail(members, includes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableSet<Account.Id> loadMembers() throws OrmException, NoSuchGroupException {
|
|
||||||
return groups.getMembers(db, groupUuid).filter(control::canSeeMember).collect(toImmutableSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableSet<AccountGroup.UUID> loadIncludes() {
|
|
||||||
if (!control.canSeeGroup()) {
|
|
||||||
return ImmutableSet.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImmutableSet.copyOf(groupIncludeCache.subgroupsOf(groupUuid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -150,7 +150,7 @@ public class GroupIncludeCacheImpl implements GroupIncludeCache {
|
|||||||
public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key)
|
public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key)
|
||||||
throws OrmException, NoSuchGroupException {
|
throws OrmException, NoSuchGroupException {
|
||||||
try (ReviewDb db = schema.open()) {
|
try (ReviewDb db = schema.open()) {
|
||||||
return groups.getIncludes(db, key).collect(toImmutableList());
|
return groups.getSubgroups(db, key).collect(toImmutableList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,16 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.account;
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
import com.google.gerrit.common.data.GroupDetail;
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroupDescription;
|
||||||
import com.google.gerrit.server.group.SystemGroupBackend;
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
@@ -29,6 +33,7 @@ import com.google.inject.assistedinject.Assisted;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class GroupMembers {
|
public class GroupMembers {
|
||||||
@@ -37,20 +42,20 @@ public class GroupMembers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final GroupCache groupCache;
|
private final GroupCache groupCache;
|
||||||
private final GroupDetailFactory.Factory groupDetailFactory;
|
private final GroupControl.Factory groupControlFactory;
|
||||||
private final AccountCache accountCache;
|
private final AccountCache accountCache;
|
||||||
private final ProjectControl.GenericFactory projectControl;
|
private final ProjectControl.GenericFactory projectControl;
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GroupMembers(
|
GroupMembers(
|
||||||
final GroupCache groupCache,
|
GroupCache groupCache,
|
||||||
final GroupDetailFactory.Factory groupDetailFactory,
|
GroupControl.Factory groupControlFactory,
|
||||||
final AccountCache accountCache,
|
AccountCache accountCache,
|
||||||
final ProjectControl.GenericFactory projectControl,
|
ProjectControl.GenericFactory projectControl,
|
||||||
@Assisted final CurrentUser currentUser) {
|
@Assisted CurrentUser currentUser) {
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.groupDetailFactory = groupDetailFactory;
|
this.groupControlFactory = groupControlFactory;
|
||||||
this.accountCache = accountCache;
|
this.accountCache = accountCache;
|
||||||
this.projectControl = projectControl;
|
this.projectControl = projectControl;
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
@@ -69,9 +74,9 @@ public class GroupMembers {
|
|||||||
if (SystemGroupBackend.PROJECT_OWNERS.equals(groupUUID)) {
|
if (SystemGroupBackend.PROJECT_OWNERS.equals(groupUUID)) {
|
||||||
return getProjectOwners(project, seen);
|
return getProjectOwners(project, seen);
|
||||||
}
|
}
|
||||||
AccountGroup group = groupCache.get(groupUUID);
|
Optional<InternalGroup> group = groupCache.get(groupUUID);
|
||||||
if (group != null) {
|
if (group.isPresent()) {
|
||||||
return getGroupMembers(group, project, seen);
|
return getGroupMembers(group.get(), project, seen);
|
||||||
}
|
}
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
@@ -96,22 +101,29 @@ public class GroupMembers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Set<Account> getGroupMembers(
|
private Set<Account> getGroupMembers(
|
||||||
final AccountGroup group, Project.NameKey project, Set<AccountGroup.UUID> seen)
|
InternalGroup group, Project.NameKey project, Set<AccountGroup.UUID> seen)
|
||||||
throws NoSuchGroupException, OrmException, NoSuchProjectException, IOException {
|
throws NoSuchGroupException, OrmException, NoSuchProjectException, IOException {
|
||||||
seen.add(group.getGroupUUID());
|
seen.add(group.getGroupUUID());
|
||||||
final GroupDetail groupDetail = groupDetailFactory.create(group.getGroupUUID()).call();
|
GroupControl groupControl = groupControlFactory.controlFor(new InternalGroupDescription(group));
|
||||||
|
|
||||||
final Set<Account> members = new HashSet<>();
|
Set<Account> directMembers =
|
||||||
for (Account.Id memberId : groupDetail.getMembers()) {
|
group
|
||||||
members.add(accountCache.get(memberId).getAccount());
|
.getMembers()
|
||||||
}
|
.stream()
|
||||||
|
.filter(groupControl::canSeeMember)
|
||||||
|
.map(accountCache::get)
|
||||||
|
.map(AccountState::getAccount)
|
||||||
|
.collect(toImmutableSet());
|
||||||
|
|
||||||
for (AccountGroup.UUID groupIncludeUuid : groupDetail.getIncludes()) {
|
Set<Account> indirectMembers = new HashSet<>();
|
||||||
AccountGroup includedGroup = groupCache.get(groupIncludeUuid);
|
if (groupControl.canSeeGroup()) {
|
||||||
if (includedGroup != null && !seen.contains(includedGroup.getGroupUUID())) {
|
for (AccountGroup.UUID subgroupUuid : group.getSubgroups()) {
|
||||||
members.addAll(listAccounts(includedGroup.getGroupUUID(), project, seen));
|
if (!seen.contains(subgroupUuid)) {
|
||||||
|
indirectMembers.addAll(listAccounts(subgroupUuid, project, seen));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return members;
|
|
||||||
|
return Sets.union(directMembers, indirectMembers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ import com.google.common.collect.Lists;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
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.group.InternalGroup;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@@ -91,11 +93,11 @@ public class IncludingGroupMembership implements GroupMembership {
|
|||||||
}
|
}
|
||||||
|
|
||||||
memberOf.put(id, false);
|
memberOf.put(id, false);
|
||||||
AccountGroup group = groupCache.get(id);
|
Optional<InternalGroup> group = groupCache.get(id);
|
||||||
if (group == null) {
|
if (!group.isPresent()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (search(includeCache.subgroupsOf(id))) {
|
if (search(group.get().getSubgroups())) {
|
||||||
memberOf.put(id, true);
|
memberOf.put(id, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ package com.google.gerrit.server.account;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import com.google.gerrit.common.data.GroupDescription;
|
import com.google.gerrit.common.data.GroupDescription;
|
||||||
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.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.group.InternalGroupDescription;
|
||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -56,11 +56,7 @@ public class InternalGroupBackend implements GroupBackend {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountGroup g = groupCache.get(uuid);
|
return groupCache.get(uuid).map(InternalGroupDescription::new).orElse(null);
|
||||||
if (g == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return GroupDescriptions.forAccountGroup(g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ import com.google.gerrit.extensions.common.GroupAuditEventInfo;
|
|||||||
import com.google.gerrit.extensions.common.GroupInfo;
|
import com.google.gerrit.extensions.common.GroupInfo;
|
||||||
import com.google.gerrit.extensions.common.GroupOptionsInfo;
|
import com.google.gerrit.extensions.common.GroupOptionsInfo;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups;
|
|
||||||
import com.google.gerrit.server.group.AddMembers;
|
import com.google.gerrit.server.group.AddMembers;
|
||||||
import com.google.gerrit.server.group.DeleteIncludedGroups;
|
import com.google.gerrit.server.group.AddSubgroups;
|
||||||
import com.google.gerrit.server.group.DeleteMembers;
|
import com.google.gerrit.server.group.DeleteMembers;
|
||||||
|
import com.google.gerrit.server.group.DeleteSubgroups;
|
||||||
import com.google.gerrit.server.group.GetAuditLog;
|
import com.google.gerrit.server.group.GetAuditLog;
|
||||||
import com.google.gerrit.server.group.GetDescription;
|
import com.google.gerrit.server.group.GetDescription;
|
||||||
import com.google.gerrit.server.group.GetDetail;
|
import com.google.gerrit.server.group.GetDetail;
|
||||||
@@ -35,8 +35,8 @@ import com.google.gerrit.server.group.GetOptions;
|
|||||||
import com.google.gerrit.server.group.GetOwner;
|
import com.google.gerrit.server.group.GetOwner;
|
||||||
import com.google.gerrit.server.group.GroupResource;
|
import com.google.gerrit.server.group.GroupResource;
|
||||||
import com.google.gerrit.server.group.Index;
|
import com.google.gerrit.server.group.Index;
|
||||||
import com.google.gerrit.server.group.ListIncludedGroups;
|
|
||||||
import com.google.gerrit.server.group.ListMembers;
|
import com.google.gerrit.server.group.ListMembers;
|
||||||
|
import com.google.gerrit.server.group.ListSubgroups;
|
||||||
import com.google.gerrit.server.group.PutDescription;
|
import com.google.gerrit.server.group.PutDescription;
|
||||||
import com.google.gerrit.server.group.PutName;
|
import com.google.gerrit.server.group.PutName;
|
||||||
import com.google.gerrit.server.group.PutOptions;
|
import com.google.gerrit.server.group.PutOptions;
|
||||||
@@ -64,9 +64,9 @@ class GroupApiImpl implements GroupApi {
|
|||||||
private final ListMembers listMembers;
|
private final ListMembers listMembers;
|
||||||
private final AddMembers addMembers;
|
private final AddMembers addMembers;
|
||||||
private final DeleteMembers deleteMembers;
|
private final DeleteMembers deleteMembers;
|
||||||
private final ListIncludedGroups listGroups;
|
private final ListSubgroups listSubgroups;
|
||||||
private final AddIncludedGroups addGroups;
|
private final AddSubgroups addSubgroups;
|
||||||
private final DeleteIncludedGroups deleteGroups;
|
private final DeleteSubgroups deleteSubgroups;
|
||||||
private final GetAuditLog getAuditLog;
|
private final GetAuditLog getAuditLog;
|
||||||
private final GroupResource rsrc;
|
private final GroupResource rsrc;
|
||||||
private final Index index;
|
private final Index index;
|
||||||
@@ -86,9 +86,9 @@ class GroupApiImpl implements GroupApi {
|
|||||||
ListMembers listMembers,
|
ListMembers listMembers,
|
||||||
AddMembers addMembers,
|
AddMembers addMembers,
|
||||||
DeleteMembers deleteMembers,
|
DeleteMembers deleteMembers,
|
||||||
ListIncludedGroups listGroups,
|
ListSubgroups listSubgroups,
|
||||||
AddIncludedGroups addGroups,
|
AddSubgroups addSubgroups,
|
||||||
DeleteIncludedGroups deleteGroups,
|
DeleteSubgroups deleteSubgroups,
|
||||||
GetAuditLog getAuditLog,
|
GetAuditLog getAuditLog,
|
||||||
Index index,
|
Index index,
|
||||||
@Assisted GroupResource rsrc) {
|
@Assisted GroupResource rsrc) {
|
||||||
@@ -105,9 +105,9 @@ class GroupApiImpl implements GroupApi {
|
|||||||
this.listMembers = listMembers;
|
this.listMembers = listMembers;
|
||||||
this.addMembers = addMembers;
|
this.addMembers = addMembers;
|
||||||
this.deleteMembers = deleteMembers;
|
this.deleteMembers = deleteMembers;
|
||||||
this.listGroups = listGroups;
|
this.listSubgroups = listSubgroups;
|
||||||
this.addGroups = addGroups;
|
this.addSubgroups = addSubgroups;
|
||||||
this.deleteGroups = deleteGroups;
|
this.deleteSubgroups = deleteSubgroups;
|
||||||
this.getAuditLog = getAuditLog;
|
this.getAuditLog = getAuditLog;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.rsrc = rsrc;
|
this.rsrc = rsrc;
|
||||||
@@ -233,27 +233,27 @@ class GroupApiImpl implements GroupApi {
|
|||||||
@Override
|
@Override
|
||||||
public List<GroupInfo> includedGroups() throws RestApiException {
|
public List<GroupInfo> includedGroups() throws RestApiException {
|
||||||
try {
|
try {
|
||||||
return listGroups.apply(rsrc);
|
return listSubgroups.apply(rsrc);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw asRestApiException("Cannot list included groups", e);
|
throw asRestApiException("Cannot list subgroups", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addGroups(String... groups) throws RestApiException {
|
public void addGroups(String... groups) throws RestApiException {
|
||||||
try {
|
try {
|
||||||
addGroups.apply(rsrc, AddIncludedGroups.Input.fromGroups(Arrays.asList(groups)));
|
addSubgroups.apply(rsrc, AddSubgroups.Input.fromGroups(Arrays.asList(groups)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw asRestApiException("Cannot add group members", e);
|
throw asRestApiException("Cannot add subgroups", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeGroups(String... groups) throws RestApiException {
|
public void removeGroups(String... groups) throws RestApiException {
|
||||||
try {
|
try {
|
||||||
deleteGroups.apply(rsrc, AddIncludedGroups.Input.fromGroups(Arrays.asList(groups)));
|
deleteSubgroups.apply(rsrc, AddSubgroups.Input.fromGroups(Arrays.asList(groups)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw asRestApiException("Cannot remove group members", e);
|
throw asRestApiException("Cannot remove subgroups", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ import com.google.gerrit.server.account.ChangeUserName;
|
|||||||
import com.google.gerrit.server.account.EmailExpander;
|
import com.google.gerrit.server.account.EmailExpander;
|
||||||
import com.google.gerrit.server.account.GroupCacheImpl;
|
import com.google.gerrit.server.account.GroupCacheImpl;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.account.GroupDetailFactory;
|
|
||||||
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
|
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
|
||||||
import com.google.gerrit.server.account.GroupMembers;
|
import com.google.gerrit.server.account.GroupMembers;
|
||||||
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
|
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
|
||||||
@@ -250,7 +249,6 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
factory(ChangeData.AssistedFactory.class);
|
factory(ChangeData.AssistedFactory.class);
|
||||||
factory(ChangeJson.AssistedFactory.class);
|
factory(ChangeJson.AssistedFactory.class);
|
||||||
factory(CreateChangeSender.Factory.class);
|
factory(CreateChangeSender.Factory.class);
|
||||||
factory(GroupDetailFactory.Factory.class);
|
|
||||||
factory(GroupMembers.Factory.class);
|
factory(GroupMembers.Factory.class);
|
||||||
factory(EmailMerge.Factory.class);
|
factory(EmailMerge.Factory.class);
|
||||||
factory(MergedSender.Factory.class);
|
factory(MergedSender.Factory.class);
|
||||||
|
|||||||
@@ -29,18 +29,19 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups.Input;
|
import com.google.gerrit.server.group.AddSubgroups.Input;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AddIncludedGroups implements RestModifyView<GroupResource, Input> {
|
public class AddSubgroups implements RestModifyView<GroupResource, Input> {
|
||||||
public static class Input {
|
public static class Input {
|
||||||
@DefaultInput String _oneGroup;
|
@DefaultInput String _oneGroup;
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ public class AddIncludedGroups implements RestModifyView<GroupResource, Input> {
|
|||||||
private final GroupJson json;
|
private final GroupJson json;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AddIncludedGroups(
|
public AddSubgroups(
|
||||||
GroupsCollection groupsCollection,
|
GroupsCollection groupsCollection,
|
||||||
Provider<ReviewDb> db,
|
Provider<ReviewDb> db,
|
||||||
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider,
|
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider,
|
||||||
@@ -86,7 +87,7 @@ public class AddIncludedGroups implements RestModifyView<GroupResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public List<GroupInfo> apply(GroupResource resource, Input input)
|
public List<GroupInfo> apply(GroupResource resource, Input input)
|
||||||
throws MethodNotAllowedException, AuthException, UnprocessableEntityException, OrmException,
|
throws MethodNotAllowedException, AuthException, UnprocessableEntityException, OrmException,
|
||||||
ResourceNotFoundException {
|
ResourceNotFoundException, IOException {
|
||||||
GroupDescription.Internal group =
|
GroupDescription.Internal group =
|
||||||
resource.asInternalGroup().orElseThrow(MethodNotAllowedException::new);
|
resource.asInternalGroup().orElseThrow(MethodNotAllowedException::new);
|
||||||
input = Input.init(input);
|
input = Input.init(input);
|
||||||
@@ -97,40 +98,41 @@ public class AddIncludedGroups implements RestModifyView<GroupResource, Input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<GroupInfo> result = new ArrayList<>();
|
List<GroupInfo> result = new ArrayList<>();
|
||||||
Set<AccountGroup.UUID> includedGroupUuids = new HashSet<>();
|
Set<AccountGroup.UUID> subgroupUuids = new HashSet<>();
|
||||||
for (String includedGroup : input.groups) {
|
for (String subgroupIdentifier : input.groups) {
|
||||||
GroupDescription.Basic d = groupsCollection.parse(includedGroup);
|
GroupDescription.Basic subgroup = groupsCollection.parse(subgroupIdentifier);
|
||||||
includedGroupUuids.add(d.getGroupUUID());
|
subgroupUuids.add(subgroup.getGroupUUID());
|
||||||
result.add(json.format(d));
|
result.add(json.format(subgroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountGroup.UUID groupUuid = group.getGroupUUID();
|
AccountGroup.UUID groupUuid = group.getGroupUUID();
|
||||||
try {
|
try {
|
||||||
groupsUpdateProvider.get().addIncludedGroups(db.get(), groupUuid, includedGroupUuids);
|
groupsUpdateProvider.get().addSubgroups(db.get(), groupUuid, subgroupUuids);
|
||||||
} catch (NoSuchGroupException e) {
|
} catch (NoSuchGroupException e) {
|
||||||
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
|
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class PutIncludedGroup implements RestModifyView<GroupResource, PutIncludedGroup.Input> {
|
static class PutSubgroup implements RestModifyView<GroupResource, PutSubgroup.Input> {
|
||||||
static class Input {}
|
static class Input {}
|
||||||
|
|
||||||
private final AddIncludedGroups put;
|
private final AddSubgroups addSubgroups;
|
||||||
private final String id;
|
private final String id;
|
||||||
|
|
||||||
PutIncludedGroup(AddIncludedGroups put, String id) {
|
PutSubgroup(AddSubgroups addSubgroups, String id) {
|
||||||
this.put = put;
|
this.addSubgroups = addSubgroups;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupInfo apply(GroupResource resource, Input input)
|
public GroupInfo apply(GroupResource resource, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException {
|
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
|
||||||
AddIncludedGroups.Input in = new AddIncludedGroups.Input();
|
IOException {
|
||||||
|
AddSubgroups.Input in = new AddSubgroups.Input();
|
||||||
in.groups = ImmutableList.of(id);
|
in.groups = ImmutableList.of(id);
|
||||||
try {
|
try {
|
||||||
List<GroupInfo> list = put.apply(resource, in);
|
List<GroupInfo> list = addSubgroups.apply(resource, in);
|
||||||
if (list.size() == 1) {
|
if (list.size() == 1) {
|
||||||
return list.get(0);
|
return list.get(0);
|
||||||
}
|
}
|
||||||
@@ -142,18 +144,16 @@ public class AddIncludedGroups implements RestModifyView<GroupResource, Input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
static class UpdateIncludedGroup
|
static class UpdateSubgroup implements RestModifyView<SubgroupResource, PutSubgroup.Input> {
|
||||||
implements RestModifyView<IncludedGroupResource, PutIncludedGroup.Input> {
|
private final Provider<GetSubgroup> get;
|
||||||
private final Provider<GetIncludedGroup> get;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UpdateIncludedGroup(Provider<GetIncludedGroup> get) {
|
UpdateSubgroup(Provider<GetSubgroup> get) {
|
||||||
this.get = get;
|
this.get = get;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupInfo apply(IncludedGroupResource resource, PutIncludedGroup.Input input)
|
public GroupInfo apply(SubgroupResource resource, PutSubgroup.Input input) throws OrmException {
|
||||||
throws OrmException {
|
|
||||||
// Do nothing, the group is already included.
|
// Do nothing, the group is already included.
|
||||||
return get.get().apply(resource);
|
return get.get().apply(resource);
|
||||||
}
|
}
|
||||||
@@ -26,22 +26,23 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups.Input;
|
import com.google.gerrit.server.group.AddSubgroups.Input;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class DeleteIncludedGroups implements RestModifyView<GroupResource, Input> {
|
public class DeleteSubgroups implements RestModifyView<GroupResource, Input> {
|
||||||
private final GroupsCollection groupsCollection;
|
private final GroupsCollection groupsCollection;
|
||||||
private final Provider<ReviewDb> db;
|
private final Provider<ReviewDb> db;
|
||||||
private final Provider<GroupsUpdate> groupsUpdateProvider;
|
private final Provider<GroupsUpdate> groupsUpdateProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DeleteIncludedGroups(
|
DeleteSubgroups(
|
||||||
GroupsCollection groupsCollection,
|
GroupsCollection groupsCollection,
|
||||||
Provider<ReviewDb> db,
|
Provider<ReviewDb> db,
|
||||||
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
|
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
|
||||||
@@ -53,7 +54,7 @@ public class DeleteIncludedGroups implements RestModifyView<GroupResource, Input
|
|||||||
@Override
|
@Override
|
||||||
public Response<?> apply(GroupResource resource, Input input)
|
public Response<?> apply(GroupResource resource, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
||||||
ResourceNotFoundException {
|
ResourceNotFoundException, IOException {
|
||||||
GroupDescription.Internal internalGroup =
|
GroupDescription.Internal internalGroup =
|
||||||
resource.asInternalGroup().orElseThrow(MethodNotAllowedException::new);
|
resource.asInternalGroup().orElseThrow(MethodNotAllowedException::new);
|
||||||
input = Input.init(input);
|
input = Input.init(input);
|
||||||
@@ -64,15 +65,15 @@ public class DeleteIncludedGroups implements RestModifyView<GroupResource, Input
|
|||||||
String.format("Cannot delete groups from group %s", internalGroup.getName()));
|
String.format("Cannot delete groups from group %s", internalGroup.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<AccountGroup.UUID> internalGroupsToRemove = new HashSet<>();
|
Set<AccountGroup.UUID> subgroupsToRemove = new HashSet<>();
|
||||||
for (String includedGroup : input.groups) {
|
for (String subgroupIdentifier : input.groups) {
|
||||||
GroupDescription.Basic d = groupsCollection.parse(includedGroup);
|
GroupDescription.Basic subgroup = groupsCollection.parse(subgroupIdentifier);
|
||||||
internalGroupsToRemove.add(d.getGroupUUID());
|
subgroupsToRemove.add(subgroup.getGroupUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
|
AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
|
||||||
try {
|
try {
|
||||||
groupsUpdateProvider.get().deleteIncludedGroups(db.get(), groupUuid, internalGroupsToRemove);
|
groupsUpdateProvider.get().removeSubgroups(db.get(), groupUuid, subgroupsToRemove);
|
||||||
} catch (NoSuchGroupException e) {
|
} catch (NoSuchGroupException e) {
|
||||||
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
|
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
|
||||||
}
|
}
|
||||||
@@ -81,22 +82,21 @@ public class DeleteIncludedGroups implements RestModifyView<GroupResource, Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
static class DeleteIncludedGroup
|
static class DeleteSubgroup implements RestModifyView<SubgroupResource, DeleteSubgroup.Input> {
|
||||||
implements RestModifyView<IncludedGroupResource, DeleteIncludedGroup.Input> {
|
|
||||||
static class Input {}
|
static class Input {}
|
||||||
|
|
||||||
private final Provider<DeleteIncludedGroups> delete;
|
private final Provider<DeleteSubgroups> delete;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DeleteIncludedGroup(Provider<DeleteIncludedGroups> delete) {
|
DeleteSubgroup(Provider<DeleteSubgroups> delete) {
|
||||||
this.delete = delete;
|
this.delete = delete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(IncludedGroupResource resource, Input input)
|
public Response<?> apply(SubgroupResource resource, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
||||||
ResourceNotFoundException {
|
ResourceNotFoundException, IOException {
|
||||||
AddIncludedGroups.Input in = new AddIncludedGroups.Input();
|
AddSubgroups.Input in = new AddSubgroups.Input();
|
||||||
in.groups = ImmutableList.of(resource.getMember().get());
|
in.groups = ImmutableList.of(resource.getMember().get());
|
||||||
return delete.get().apply(resource, in);
|
return delete.get().apply(resource, in);
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
package com.google.gerrit.server.group;
|
package com.google.gerrit.server.group;
|
||||||
|
|
||||||
import com.google.gerrit.common.data.GroupDescription;
|
import com.google.gerrit.common.data.GroupDescription;
|
||||||
import com.google.gerrit.common.data.GroupDescriptions;
|
|
||||||
import com.google.gerrit.extensions.common.AccountInfo;
|
import com.google.gerrit.extensions.common.AccountInfo;
|
||||||
import com.google.gerrit.extensions.common.GroupAuditEventInfo;
|
import com.google.gerrit.extensions.common.GroupAuditEventInfo;
|
||||||
import com.google.gerrit.extensions.common.GroupInfo;
|
import com.google.gerrit.extensions.common.GroupInfo;
|
||||||
@@ -38,6 +37,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class GetAuditLog implements RestReadView<GroupResource> {
|
public class GetAuditLog implements RestReadView<GroupResource> {
|
||||||
@@ -94,10 +94,10 @@ public class GetAuditLog implements RestReadView<GroupResource> {
|
|||||||
for (AccountGroupByIdAud auditEvent :
|
for (AccountGroupByIdAud auditEvent :
|
||||||
db.get().accountGroupByIdAud().byGroup(group.getId()).toList()) {
|
db.get().accountGroupByIdAud().byGroup(group.getId()).toList()) {
|
||||||
AccountGroup.UUID includedGroupUUID = auditEvent.getKey().getIncludeUUID();
|
AccountGroup.UUID includedGroupUUID = auditEvent.getKey().getIncludeUUID();
|
||||||
AccountGroup includedGroup = groupCache.get(includedGroupUUID);
|
Optional<InternalGroup> includedGroup = groupCache.get(includedGroupUUID);
|
||||||
GroupInfo member;
|
GroupInfo member;
|
||||||
if (includedGroup != null) {
|
if (includedGroup.isPresent()) {
|
||||||
member = groupJson.format(GroupDescriptions.forAccountGroup(includedGroup));
|
member = groupJson.format(new InternalGroupDescription(includedGroup.get()));
|
||||||
} else {
|
} else {
|
||||||
GroupDescription.Basic groupDescription = groupBackend.get(includedGroupUUID);
|
GroupDescription.Basic groupDescription = groupBackend.get(includedGroupUUID);
|
||||||
member = new GroupInfo();
|
member = new GroupInfo();
|
||||||
|
|||||||
@@ -21,16 +21,16 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class GetIncludedGroup implements RestReadView<IncludedGroupResource> {
|
public class GetSubgroup implements RestReadView<SubgroupResource> {
|
||||||
private final GroupJson json;
|
private final GroupJson json;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GetIncludedGroup(GroupJson json) {
|
GetSubgroup(GroupJson json) {
|
||||||
this.json = json;
|
this.json = json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupInfo apply(IncludedGroupResource rsrc) throws OrmException {
|
public GroupInfo apply(SubgroupResource rsrc) throws OrmException {
|
||||||
return json.format(rsrc.getMemberDescription());
|
return json.format(rsrc.getMemberDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ public class GroupJson {
|
|||||||
private final GroupBackend groupBackend;
|
private final GroupBackend groupBackend;
|
||||||
private final GroupControl.Factory groupControlFactory;
|
private final GroupControl.Factory groupControlFactory;
|
||||||
private final Provider<ListMembers> listMembers;
|
private final Provider<ListMembers> listMembers;
|
||||||
private final Provider<ListIncludedGroups> listIncludes;
|
private final Provider<ListSubgroups> listSubgroups;
|
||||||
private EnumSet<ListGroupsOption> options;
|
private EnumSet<ListGroupsOption> options;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -53,11 +53,11 @@ public class GroupJson {
|
|||||||
GroupBackend groupBackend,
|
GroupBackend groupBackend,
|
||||||
GroupControl.Factory groupControlFactory,
|
GroupControl.Factory groupControlFactory,
|
||||||
Provider<ListMembers> listMembers,
|
Provider<ListMembers> listMembers,
|
||||||
Provider<ListIncludedGroups> listIncludes) {
|
Provider<ListSubgroups> listSubgroups) {
|
||||||
this.groupBackend = groupBackend;
|
this.groupBackend = groupBackend;
|
||||||
this.groupControlFactory = groupControlFactory;
|
this.groupControlFactory = groupControlFactory;
|
||||||
this.listMembers = listMembers;
|
this.listMembers = listMembers;
|
||||||
this.listIncludes = listIncludes;
|
this.listSubgroups = listSubgroups;
|
||||||
|
|
||||||
options = EnumSet.noneOf(ListGroupsOption.class);
|
options = EnumSet.noneOf(ListGroupsOption.class);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ public class GroupJson {
|
|||||||
|
|
||||||
public GroupInfo format(GroupResource rsrc) throws OrmException {
|
public GroupInfo format(GroupResource rsrc) throws OrmException {
|
||||||
GroupInfo info = init(rsrc.getGroup());
|
GroupInfo info = init(rsrc.getGroup());
|
||||||
initMembersAndIncludes(rsrc, info);
|
initMembersAndSubgroups(rsrc, info);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ public class GroupJson {
|
|||||||
GroupInfo info = init(group);
|
GroupInfo info = init(group);
|
||||||
if (options.contains(MEMBERS) || options.contains(INCLUDES)) {
|
if (options.contains(MEMBERS) || options.contains(INCLUDES)) {
|
||||||
GroupResource rsrc = new GroupResource(groupControlFactory.controlFor(group));
|
GroupResource rsrc = new GroupResource(groupControlFactory.controlFor(group));
|
||||||
initMembersAndIncludes(rsrc, info);
|
initMembersAndSubgroups(rsrc, info);
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,8 @@ public class GroupJson {
|
|||||||
return group instanceof GroupDescription.Internal;
|
return group instanceof GroupDescription.Internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupInfo initMembersAndIncludes(GroupResource rsrc, GroupInfo info) throws OrmException {
|
private GroupInfo initMembersAndSubgroups(GroupResource rsrc, GroupInfo info)
|
||||||
|
throws OrmException {
|
||||||
if (!rsrc.isInternalGroup()) {
|
if (!rsrc.isInternalGroup()) {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@@ -126,7 +127,7 @@ public class GroupJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.contains(INCLUDES)) {
|
if (options.contains(INCLUDES)) {
|
||||||
info.includes = listIncludes.get().apply(rsrc);
|
info.includes = listSubgroups.get().apply(rsrc);
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
} catch (MethodNotAllowedException e) {
|
} catch (MethodNotAllowedException e) {
|
||||||
|
|||||||
@@ -145,16 +145,16 @@ public class Groups {
|
|||||||
*
|
*
|
||||||
* @param db the {@code ReviewDb} instance to use for lookups
|
* @param db the {@code ReviewDb} instance to use for lookups
|
||||||
* @param parentGroupUuid the UUID of the parent group
|
* @param parentGroupUuid the UUID of the parent group
|
||||||
* @param includedGroupUuid the UUID of the subgroup
|
* @param subgroupUuid the UUID of the subgroup
|
||||||
* @return {@code true} if the group is a subgroup of the other group, or else {@code false}
|
* @return {@code true} if the group is a subgroup of the other group, or else {@code false}
|
||||||
* @throws OrmException if an error occurs while reading from ReviewDb
|
* @throws OrmException if an error occurs while reading from ReviewDb
|
||||||
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
||||||
*/
|
*/
|
||||||
public boolean isIncluded(
|
public boolean isSubgroup(
|
||||||
ReviewDb db, AccountGroup.UUID parentGroupUuid, AccountGroup.UUID includedGroupUuid)
|
ReviewDb db, AccountGroup.UUID parentGroupUuid, AccountGroup.UUID subgroupUuid)
|
||||||
throws OrmException, NoSuchGroupException {
|
throws OrmException, NoSuchGroupException {
|
||||||
AccountGroup parentGroup = getExistingGroup(db, parentGroupUuid);
|
AccountGroup parentGroup = getExistingGroup(db, parentGroupUuid);
|
||||||
AccountGroupById.Key key = new AccountGroupById.Key(parentGroup.getId(), includedGroupUuid);
|
AccountGroupById.Key key = new AccountGroupById.Key(parentGroup.getId(), subgroupUuid);
|
||||||
return db.accountGroupById().get(key) != null;
|
return db.accountGroupById().get(key) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ public class Groups {
|
|||||||
* @throws OrmException if an error occurs while reading from ReviewDb
|
* @throws OrmException if an error occurs while reading from ReviewDb
|
||||||
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
||||||
*/
|
*/
|
||||||
public Stream<AccountGroup.UUID> getIncludes(ReviewDb db, AccountGroup.UUID groupUuid)
|
public Stream<AccountGroup.UUID> getSubgroups(ReviewDb db, AccountGroup.UUID groupUuid)
|
||||||
throws OrmException, NoSuchGroupException {
|
throws OrmException, NoSuchGroupException {
|
||||||
AccountGroup group = getExistingGroup(db, groupUuid);
|
AccountGroup group = getExistingGroup(db, groupUuid);
|
||||||
ResultSet<AccountGroupById> accountGroupByIds = db.accountGroupById().byGroup(group.getId());
|
ResultSet<AccountGroupById> accountGroupByIds = db.accountGroupById().byGroup(group.getId());
|
||||||
@@ -226,14 +226,14 @@ public class Groups {
|
|||||||
* exist. This method doesn't check whether the parent groups exist.
|
* exist. This method doesn't check whether the parent groups exist.
|
||||||
*
|
*
|
||||||
* @param db the {@code ReviewDb} instance to use for lookups
|
* @param db the {@code ReviewDb} instance to use for lookups
|
||||||
* @param includedGroupUuid the UUID of the subgroup
|
* @param subgroupUuid the UUID of the subgroup
|
||||||
* @return a stream of the IDs of the parent groups
|
* @return a stream of the IDs of the parent groups
|
||||||
* @throws OrmException if an error occurs while reading from ReviewDb
|
* @throws OrmException if an error occurs while reading from ReviewDb
|
||||||
*/
|
*/
|
||||||
public Stream<AccountGroup.Id> getParentGroups(ReviewDb db, AccountGroup.UUID includedGroupUuid)
|
public Stream<AccountGroup.Id> getParentGroups(ReviewDb db, AccountGroup.UUID subgroupUuid)
|
||||||
throws OrmException {
|
throws OrmException {
|
||||||
ResultSet<AccountGroupById> accountGroupByIds =
|
ResultSet<AccountGroupById> accountGroupByIds =
|
||||||
db.accountGroupById().byIncludeUUID(includedGroupUuid);
|
db.accountGroupById().byIncludeUUID(subgroupUuid);
|
||||||
return Streams.stream(accountGroupByIds).map(AccountGroupById::getGroupId);
|
return Streams.stream(accountGroupByIds).map(AccountGroupById::getGroupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ public class GroupsUpdate {
|
|||||||
ReviewDb db, AccountGroup.UUID groupUuid, Consumer<AccountGroup> groupConsumer)
|
ReviewDb db, AccountGroup.UUID groupUuid, Consumer<AccountGroup> groupConsumer)
|
||||||
throws OrmException, IOException, NoSuchGroupException {
|
throws OrmException, IOException, NoSuchGroupException {
|
||||||
AccountGroup updatedGroup = updateGroupInDb(db, groupUuid, groupConsumer);
|
AccountGroup updatedGroup = updateGroupInDb(db, groupUuid, groupConsumer);
|
||||||
groupCache.evict(updatedGroup);
|
groupCache.evict(updatedGroup.getGroupUUID(), updatedGroup.getId(), updatedGroup.getNameKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -221,7 +221,7 @@ public class GroupsUpdate {
|
|||||||
|
|
||||||
db.accountGroupNames().deleteKeys(ImmutableList.of(oldName));
|
db.accountGroupNames().deleteKeys(ImmutableList.of(oldName));
|
||||||
|
|
||||||
groupCache.evict(group);
|
groupCache.evict(group.getGroupUUID(), group.getId(), group.getNameKey());
|
||||||
groupCache.evictAfterRename(oldName, newName);
|
groupCache.evictAfterRename(oldName, newName);
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -259,7 +259,7 @@ public class GroupsUpdate {
|
|||||||
* @param groupUuid the UUID of the group
|
* @param groupUuid the UUID of the group
|
||||||
* @param accountIds a set of IDs of accounts to add
|
* @param accountIds a set of IDs of accounts to add
|
||||||
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
||||||
* @throws IOException if the cache entry of one of the new members couldn't be invalidated
|
* @throws IOException if the group or one of the new members couldn't be indexed
|
||||||
* @throws NoSuchGroupException if the specified group doesn't exist
|
* @throws NoSuchGroupException if the specified group doesn't exist
|
||||||
*/
|
*/
|
||||||
public void addGroupMembers(ReviewDb db, AccountGroup.UUID groupUuid, Set<Account.Id> accountIds)
|
public void addGroupMembers(ReviewDb db, AccountGroup.UUID groupUuid, Set<Account.Id> accountIds)
|
||||||
@@ -293,6 +293,7 @@ public class GroupsUpdate {
|
|||||||
auditService.dispatchAddAccountsToGroup(currentUser.getAccountId(), newMembers);
|
auditService.dispatchAddAccountsToGroup(currentUser.getAccountId(), newMembers);
|
||||||
}
|
}
|
||||||
db.accountGroupMembers().insert(newMembers);
|
db.accountGroupMembers().insert(newMembers);
|
||||||
|
groupCache.evict(group.getGroupUUID(), group.getId(), group.getNameKey());
|
||||||
for (AccountGroupMember newMember : newMembers) {
|
for (AccountGroupMember newMember : newMembers) {
|
||||||
accountCache.evict(newMember.getAccountId());
|
accountCache.evict(newMember.getAccountId());
|
||||||
}
|
}
|
||||||
@@ -306,7 +307,7 @@ public class GroupsUpdate {
|
|||||||
* @param groupUuid the UUID of the group
|
* @param groupUuid the UUID of the group
|
||||||
* @param accountIds a set of IDs of accounts to remove
|
* @param accountIds a set of IDs of accounts to remove
|
||||||
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
||||||
* @throws IOException if the cache entry of one of the removed members couldn't be invalidated
|
* @throws IOException if the group or one of the removed members couldn't be indexed
|
||||||
* @throws NoSuchGroupException if the specified group doesn't exist
|
* @throws NoSuchGroupException if the specified group doesn't exist
|
||||||
*/
|
*/
|
||||||
public void removeGroupMembers(
|
public void removeGroupMembers(
|
||||||
@@ -331,6 +332,7 @@ public class GroupsUpdate {
|
|||||||
auditService.dispatchDeleteAccountsFromGroup(currentUser.getAccountId(), membersToRemove);
|
auditService.dispatchDeleteAccountsFromGroup(currentUser.getAccountId(), membersToRemove);
|
||||||
}
|
}
|
||||||
db.accountGroupMembers().delete(membersToRemove);
|
db.accountGroupMembers().delete(membersToRemove);
|
||||||
|
groupCache.evict(group.getGroupUUID(), group.getId(), group.getNameKey());
|
||||||
for (AccountGroupMember member : membersToRemove) {
|
for (AccountGroupMember member : membersToRemove) {
|
||||||
accountCache.evict(member.getAccountId());
|
accountCache.evict(member.getAccountId());
|
||||||
}
|
}
|
||||||
@@ -347,33 +349,35 @@ public class GroupsUpdate {
|
|||||||
*
|
*
|
||||||
* @param db the {@code ReviewDb} instance to update
|
* @param db the {@code ReviewDb} instance to update
|
||||||
* @param parentGroupUuid the UUID of the parent group
|
* @param parentGroupUuid the UUID of the parent group
|
||||||
* @param includedGroupUuids a set of IDs of the groups to add as subgroups
|
* @param subgroupUuids a set of IDs of the groups to add as subgroups
|
||||||
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
||||||
|
* @throws IOException if the parent group couldn't be indexed
|
||||||
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
||||||
*/
|
*/
|
||||||
public void addIncludedGroups(
|
public void addSubgroups(
|
||||||
ReviewDb db, AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> includedGroupUuids)
|
ReviewDb db, AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> subgroupUuids)
|
||||||
throws OrmException, NoSuchGroupException {
|
throws OrmException, NoSuchGroupException, IOException {
|
||||||
AccountGroup parentGroup = groups.getExistingGroup(db, parentGroupUuid);
|
AccountGroup parentGroup = groups.getExistingGroup(db, parentGroupUuid);
|
||||||
AccountGroup.Id parentGroupId = parentGroup.getId();
|
AccountGroup.Id parentGroupId = parentGroup.getId();
|
||||||
Set<AccountGroupById> newIncludedGroups = new HashSet<>();
|
Set<AccountGroupById> newSubgroups = new HashSet<>();
|
||||||
for (AccountGroup.UUID includedGroupUuid : includedGroupUuids) {
|
for (AccountGroup.UUID includedGroupUuid : subgroupUuids) {
|
||||||
boolean isIncluded = groups.isIncluded(db, parentGroupUuid, includedGroupUuid);
|
boolean isSubgroup = groups.isSubgroup(db, parentGroupUuid, includedGroupUuid);
|
||||||
if (!isIncluded) {
|
if (!isSubgroup) {
|
||||||
AccountGroupById.Key key = new AccountGroupById.Key(parentGroupId, includedGroupUuid);
|
AccountGroupById.Key key = new AccountGroupById.Key(parentGroupId, includedGroupUuid);
|
||||||
newIncludedGroups.add(new AccountGroupById(key));
|
newSubgroups.add(new AccountGroupById(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newIncludedGroups.isEmpty()) {
|
if (newSubgroups.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
auditService.dispatchAddGroupsToGroup(currentUser.getAccountId(), newIncludedGroups);
|
auditService.dispatchAddGroupsToGroup(currentUser.getAccountId(), newSubgroups);
|
||||||
}
|
}
|
||||||
db.accountGroupById().insert(newIncludedGroups);
|
db.accountGroupById().insert(newSubgroups);
|
||||||
for (AccountGroupById newIncludedGroup : newIncludedGroups) {
|
groupCache.evict(parentGroup.getGroupUUID(), parentGroup.getId(), parentGroup.getNameKey());
|
||||||
|
for (AccountGroupById newIncludedGroup : newSubgroups) {
|
||||||
groupIncludeCache.evictParentGroupsOf(newIncludedGroup.getIncludeUUID());
|
groupIncludeCache.evictParentGroupsOf(newIncludedGroup.getIncludeUUID());
|
||||||
}
|
}
|
||||||
groupIncludeCache.evictSubgroupsOf(parentGroupUuid);
|
groupIncludeCache.evictSubgroupsOf(parentGroupUuid);
|
||||||
@@ -388,34 +392,35 @@ public class GroupsUpdate {
|
|||||||
*
|
*
|
||||||
* @param db the {@code ReviewDb} instance to update
|
* @param db the {@code ReviewDb} instance to update
|
||||||
* @param parentGroupUuid the UUID of the parent group
|
* @param parentGroupUuid the UUID of the parent group
|
||||||
* @param includedGroupUuids a set of IDs of the subgroups to remove from the parent group
|
* @param subgroupUuids a set of IDs of the subgroups to remove from the parent group
|
||||||
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
* @throws OrmException if an error occurs while reading/writing from/to ReviewDb
|
||||||
|
* @throws IOException if the parent group couldn't be indexed
|
||||||
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
* @throws NoSuchGroupException if the specified parent group doesn't exist
|
||||||
*/
|
*/
|
||||||
public void deleteIncludedGroups(
|
public void removeSubgroups(
|
||||||
ReviewDb db, AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> includedGroupUuids)
|
ReviewDb db, AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> subgroupUuids)
|
||||||
throws OrmException, NoSuchGroupException {
|
throws OrmException, NoSuchGroupException, IOException {
|
||||||
AccountGroup parentGroup = groups.getExistingGroup(db, parentGroupUuid);
|
AccountGroup parentGroup = groups.getExistingGroup(db, parentGroupUuid);
|
||||||
AccountGroup.Id parentGroupId = parentGroup.getId();
|
AccountGroup.Id parentGroupId = parentGroup.getId();
|
||||||
Set<AccountGroupById> includedGroupsToRemove = new HashSet<>();
|
Set<AccountGroupById> subgroupsToRemove = new HashSet<>();
|
||||||
for (AccountGroup.UUID includedGroupUuid : includedGroupUuids) {
|
for (AccountGroup.UUID subgroupUuid : subgroupUuids) {
|
||||||
boolean isIncluded = groups.isIncluded(db, parentGroupUuid, includedGroupUuid);
|
boolean isSubgroup = groups.isSubgroup(db, parentGroupUuid, subgroupUuid);
|
||||||
if (isIncluded) {
|
if (isSubgroup) {
|
||||||
AccountGroupById.Key key = new AccountGroupById.Key(parentGroupId, includedGroupUuid);
|
AccountGroupById.Key key = new AccountGroupById.Key(parentGroupId, subgroupUuid);
|
||||||
includedGroupsToRemove.add(new AccountGroupById(key));
|
subgroupsToRemove.add(new AccountGroupById(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includedGroupsToRemove.isEmpty()) {
|
if (subgroupsToRemove.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
auditService.dispatchDeleteGroupsFromGroup(
|
auditService.dispatchDeleteGroupsFromGroup(currentUser.getAccountId(), subgroupsToRemove);
|
||||||
currentUser.getAccountId(), includedGroupsToRemove);
|
|
||||||
}
|
}
|
||||||
db.accountGroupById().delete(includedGroupsToRemove);
|
db.accountGroupById().delete(subgroupsToRemove);
|
||||||
for (AccountGroupById groupToRemove : includedGroupsToRemove) {
|
groupCache.evict(parentGroup.getGroupUUID(), parentGroup.getId(), parentGroup.getNameKey());
|
||||||
|
for (AccountGroupById groupToRemove : subgroupsToRemove) {
|
||||||
groupIncludeCache.evictParentGroupsOf(groupToRemove.getIncludeUUID());
|
groupIncludeCache.evictParentGroupsOf(groupToRemove.getIncludeUUID());
|
||||||
}
|
}
|
||||||
groupIncludeCache.evictSubgroupsOf(parentGroupUuid);
|
groupIncludeCache.evictSubgroupsOf(parentGroupUuid);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.google.gerrit.server.group.Index.Input;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Index implements RestModifyView<GroupResource, Input> {
|
public class Index implements RestModifyView<GroupResource, Input> {
|
||||||
@@ -49,9 +50,11 @@ public class Index implements RestModifyView<GroupResource, Input> {
|
|||||||
String.format("External Group Not Allowed: %s", groupUuid.get()));
|
String.format("External Group Not Allowed: %s", groupUuid.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountGroup accountGroup = groupCache.get(groupUuid);
|
Optional<InternalGroup> group = groupCache.get(groupUuid);
|
||||||
// evicting the group from the cache, reindexes the group
|
// evicting the group from the cache, reindexes the group
|
||||||
groupCache.evict(accountGroup);
|
if (group.isPresent()) {
|
||||||
|
groupCache.evict(group.get().getGroupUUID(), group.get().getId(), group.get().getNameKey());
|
||||||
|
}
|
||||||
return Response.none();
|
return Response.none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (C) 2017 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.group;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
@AutoValue
|
||||||
|
public abstract class InternalGroup {
|
||||||
|
|
||||||
|
public static InternalGroup create(
|
||||||
|
AccountGroup accountGroup,
|
||||||
|
ImmutableSet<Account.Id> members,
|
||||||
|
ImmutableSet<AccountGroup.UUID> subgroups) {
|
||||||
|
return new AutoValue_InternalGroup(
|
||||||
|
accountGroup.getId(),
|
||||||
|
accountGroup.getNameKey(),
|
||||||
|
accountGroup.getDescription(),
|
||||||
|
accountGroup.getOwnerGroupUUID(),
|
||||||
|
accountGroup.isVisibleToAll(),
|
||||||
|
accountGroup.getGroupUUID(),
|
||||||
|
accountGroup.getCreatedOn(),
|
||||||
|
members,
|
||||||
|
subgroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract AccountGroup.Id getId();
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return getNameKey().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract AccountGroup.NameKey getNameKey();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract String getDescription();
|
||||||
|
|
||||||
|
public abstract AccountGroup.UUID getOwnerGroupUUID();
|
||||||
|
|
||||||
|
public abstract boolean isVisibleToAll();
|
||||||
|
|
||||||
|
public abstract AccountGroup.UUID getGroupUUID();
|
||||||
|
|
||||||
|
public abstract Timestamp getCreatedOn();
|
||||||
|
|
||||||
|
public abstract ImmutableSet<Account.Id> getMembers();
|
||||||
|
|
||||||
|
public abstract ImmutableSet<AccountGroup.UUID> getSubgroups();
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (C) 2017 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.group;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.common.PageLinks;
|
||||||
|
import com.google.gerrit.common.data.GroupDescription;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
public class InternalGroupDescription implements GroupDescription.Internal {
|
||||||
|
|
||||||
|
private final InternalGroup internalGroup;
|
||||||
|
|
||||||
|
public InternalGroupDescription(InternalGroup internalGroup) {
|
||||||
|
this.internalGroup = checkNotNull(internalGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccountGroup.UUID getGroupUUID() {
|
||||||
|
return internalGroup.getGroupUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return internalGroup.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getEmailAddress() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUrl() {
|
||||||
|
return "#" + PageLinks.toGroup(getGroupUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccountGroup.Id getId() {
|
||||||
|
return internalGroup.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String getDescription() {
|
||||||
|
return internalGroup.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccountGroup.UUID getOwnerGroupUUID() {
|
||||||
|
return internalGroup.getOwnerGroupUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVisibleToAll() {
|
||||||
|
return internalGroup.isVisibleToAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timestamp getCreatedOn() {
|
||||||
|
return internalGroup.getCreatedOn();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.group;
|
package com.google.gerrit.server.group;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
|
|
||||||
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.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.gerrit.common.data.GroupDescription;
|
import com.google.gerrit.common.data.GroupDescription;
|
||||||
@@ -35,7 +38,6 @@ 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.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.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
@@ -43,13 +45,14 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Comparator;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -58,6 +61,8 @@ import org.kohsuke.args4j.Option;
|
|||||||
|
|
||||||
/** List groups visible to the calling user. */
|
/** List groups visible to the calling user. */
|
||||||
public class ListGroups implements RestReadView<TopLevelResource> {
|
public class ListGroups implements RestReadView<TopLevelResource> {
|
||||||
|
private static final Comparator<GroupDescription.Internal> GROUP_COMPARATOR =
|
||||||
|
Comparator.comparing(GroupDescription.Basic::getName);
|
||||||
|
|
||||||
protected final GroupCache groupCache;
|
protected final GroupCache groupCache;
|
||||||
|
|
||||||
@@ -266,33 +271,32 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
|
|
||||||
private List<GroupInfo> getAllGroups() throws OrmException {
|
private List<GroupInfo> getAllGroups() throws OrmException {
|
||||||
List<GroupInfo> groupInfos;
|
List<GroupInfo> groupInfos;
|
||||||
List<AccountGroup> groupList;
|
List<GroupDescription.Internal> groupList;
|
||||||
if (!projects.isEmpty()) {
|
if (!projects.isEmpty()) {
|
||||||
Map<AccountGroup.UUID, AccountGroup> groups = new HashMap<>();
|
Map<AccountGroup.UUID, GroupDescription.Internal> groups = new HashMap<>();
|
||||||
for (ProjectControl projectControl : projects) {
|
for (ProjectControl projectControl : projects) {
|
||||||
final Set<GroupReference> groupsRefs = projectControl.getAllGroups();
|
final Set<GroupReference> groupsRefs = projectControl.getAllGroups();
|
||||||
for (GroupReference groupRef : groupsRefs) {
|
for (GroupReference groupRef : groupsRefs) {
|
||||||
final AccountGroup group = groupCache.get(groupRef.getUUID());
|
Optional<InternalGroup> internalGroup = groupCache.get(groupRef.getUUID());
|
||||||
if (group != null) {
|
internalGroup.ifPresent(
|
||||||
groups.put(group.getGroupUUID(), group);
|
group -> groups.put(group.getGroupUUID(), new InternalGroupDescription(group)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groupList = filterGroups(groups.values());
|
groupList = filterGroups(groups.values());
|
||||||
} else {
|
} else {
|
||||||
groupList = filterGroups(groupCache.all());
|
groupList = filterGroups(getAllExistingInternalGroups());
|
||||||
}
|
}
|
||||||
groupInfos = Lists.newArrayListWithCapacity(groupList.size());
|
groupInfos = Lists.newArrayListWithCapacity(groupList.size());
|
||||||
int found = 0;
|
int found = 0;
|
||||||
int foundIndex = 0;
|
int foundIndex = 0;
|
||||||
for (AccountGroup group : groupList) {
|
for (GroupDescription.Internal group : groupList) {
|
||||||
if (foundIndex++ < start) {
|
if (foundIndex++ < start) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (limit > 0 && ++found > limit) {
|
if (limit > 0 && ++found > limit) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
groupInfos.add(json.addOptions(options).format(GroupDescriptions.forAccountGroup(group)));
|
groupInfos.add(json.addOptions(options).format(group));
|
||||||
}
|
}
|
||||||
return groupInfos;
|
return groupInfos;
|
||||||
}
|
}
|
||||||
@@ -355,7 +359,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
List<GroupInfo> groups = new ArrayList<>();
|
List<GroupInfo> groups = new ArrayList<>();
|
||||||
int found = 0;
|
int found = 0;
|
||||||
int foundIndex = 0;
|
int foundIndex = 0;
|
||||||
for (AccountGroup g : filterGroups(groupCache.all())) {
|
for (GroupDescription.Internal g : filterGroups(getAllExistingInternalGroups())) {
|
||||||
GroupControl ctl = groupControlFactory.controlFor(g);
|
GroupControl ctl = groupControlFactory.controlFor(g);
|
||||||
try {
|
try {
|
||||||
if (genericGroupControlFactory.controlFor(user, g.getGroupUUID()).isOwner()) {
|
if (genericGroupControlFactory.controlFor(user, g.getGroupUUID()).isOwner()) {
|
||||||
@@ -374,10 +378,19 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AccountGroup> filterGroups(Collection<AccountGroup> groups) {
|
private ImmutableList<GroupDescription.Internal> getAllExistingInternalGroups() {
|
||||||
List<AccountGroup> filteredGroups = new ArrayList<>(groups.size());
|
return groupCache
|
||||||
|
.all()
|
||||||
|
.stream()
|
||||||
|
.map(GroupDescriptions::forAccountGroup)
|
||||||
|
.collect(toImmutableList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GroupDescription.Internal> filterGroups(
|
||||||
|
Collection<GroupDescription.Internal> groups) {
|
||||||
|
List<GroupDescription.Internal> filteredGroups = new ArrayList<>(groups.size());
|
||||||
Pattern pattern = Strings.isNullOrEmpty(matchRegex) ? null : Pattern.compile(matchRegex);
|
Pattern pattern = Strings.isNullOrEmpty(matchRegex) ? null : Pattern.compile(matchRegex);
|
||||||
for (AccountGroup group : groups) {
|
for (GroupDescription.Internal group : groups) {
|
||||||
if (!Strings.isNullOrEmpty(matchSubstring)) {
|
if (!Strings.isNullOrEmpty(matchSubstring)) {
|
||||||
if (!group
|
if (!group
|
||||||
.getName()
|
.getName()
|
||||||
@@ -402,7 +415,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
|||||||
filteredGroups.add(group);
|
filteredGroups.add(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(filteredGroups, new GroupComparator());
|
filteredGroups.sort(GROUP_COMPARATOR);
|
||||||
return filteredGroups;
|
return filteredGroups;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.group;
|
package com.google.gerrit.server.group;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import com.google.gerrit.common.data.GroupDescription;
|
import com.google.gerrit.common.data.GroupDescription;
|
||||||
import com.google.gerrit.common.data.GroupDetail;
|
|
||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
|
||||||
import com.google.gerrit.extensions.common.AccountInfo;
|
import com.google.gerrit.extensions.common.AccountInfo;
|
||||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||||
@@ -25,20 +26,20 @@ import com.google.gerrit.reviewdb.client.Account;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.account.AccountLoader;
|
import com.google.gerrit.server.account.AccountLoader;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.account.GroupDetailFactory;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.api.accounts.AccountInfoComparator;
|
import com.google.gerrit.server.api.accounts.AccountInfoComparator;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
public class ListMembers implements RestReadView<GroupResource> {
|
public class ListMembers implements RestReadView<GroupResource> {
|
||||||
private final GroupCache groupCache;
|
private final GroupCache groupCache;
|
||||||
private final GroupDetailFactory.Factory groupDetailFactory;
|
private final GroupControl.Factory groupControlFactory;
|
||||||
private final AccountLoader accountLoader;
|
private final AccountLoader accountLoader;
|
||||||
|
|
||||||
@Option(name = "--recursive", usage = "to resolve included groups recursively")
|
@Option(name = "--recursive", usage = "to resolve included groups recursively")
|
||||||
@@ -47,10 +48,10 @@ public class ListMembers implements RestReadView<GroupResource> {
|
|||||||
@Inject
|
@Inject
|
||||||
protected ListMembers(
|
protected ListMembers(
|
||||||
GroupCache groupCache,
|
GroupCache groupCache,
|
||||||
GroupDetailFactory.Factory groupDetailFactory,
|
GroupControl.Factory groupControlFactory,
|
||||||
AccountLoader.Factory accountLoaderFactory) {
|
AccountLoader.Factory accountLoaderFactory) {
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.groupDetailFactory = groupDetailFactory;
|
this.groupControlFactory = groupControlFactory;
|
||||||
this.accountLoader = accountLoaderFactory.create(true);
|
this.accountLoader = accountLoaderFactory.create(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,52 +68,40 @@ public class ListMembers implements RestReadView<GroupResource> {
|
|||||||
return apply(group.getGroupUUID());
|
return apply(group.getGroupUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AccountInfo> apply(AccountGroup group) throws OrmException {
|
|
||||||
return apply(group.getGroupUUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<AccountInfo> apply(AccountGroup.UUID groupId) throws OrmException {
|
public List<AccountInfo> apply(AccountGroup.UUID groupId) throws OrmException {
|
||||||
final Map<Account.Id, AccountInfo> members =
|
Set<Account.Id> members = getMembers(groupId, new HashSet<>());
|
||||||
getMembers(groupId, new HashSet<AccountGroup.UUID>());
|
List<AccountInfo> memberInfos = new ArrayList<>(members.size());
|
||||||
final List<AccountInfo> memberInfos = Lists.newArrayList(members.values());
|
for (Account.Id member : members) {
|
||||||
Collections.sort(memberInfos, AccountInfoComparator.ORDER_NULLS_FIRST);
|
memberInfos.add(accountLoader.get(member));
|
||||||
|
}
|
||||||
|
accountLoader.fill();
|
||||||
|
memberInfos.sort(AccountInfoComparator.ORDER_NULLS_FIRST);
|
||||||
return memberInfos;
|
return memberInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Account.Id, AccountInfo> getMembers(
|
private Set<Account.Id> getMembers(
|
||||||
final AccountGroup.UUID groupUUID, HashSet<AccountGroup.UUID> seenGroups)
|
AccountGroup.UUID groupUUID, HashSet<AccountGroup.UUID> seenGroups) {
|
||||||
throws OrmException {
|
|
||||||
seenGroups.add(groupUUID);
|
seenGroups.add(groupUUID);
|
||||||
|
|
||||||
final Map<Account.Id, AccountInfo> members = new HashMap<>();
|
Optional<InternalGroup> internalGroup = groupCache.get(groupUUID);
|
||||||
final AccountGroup group = groupCache.get(groupUUID);
|
if (!internalGroup.isPresent()) {
|
||||||
if (group == null) {
|
return ImmutableSet.of();
|
||||||
// the included group is an external group and can't be resolved
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
}
|
||||||
|
InternalGroup group = internalGroup.get();
|
||||||
|
|
||||||
final GroupDetail groupDetail;
|
GroupControl groupControl = groupControlFactory.controlFor(new InternalGroupDescription(group));
|
||||||
try {
|
|
||||||
groupDetail = groupDetailFactory.create(group.getGroupUUID()).call();
|
|
||||||
} catch (NoSuchGroupException e) {
|
|
||||||
// the included group is not visible
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Account.Id member : groupDetail.getMembers()) {
|
Set<Account.Id> directMembers =
|
||||||
if (!members.containsKey(member)) {
|
group.getMembers().stream().filter(groupControl::canSeeMember).collect(toImmutableSet());
|
||||||
members.put(member, accountLoader.get(member));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recursive) {
|
Set<Account.Id> indirectMembers = new HashSet<>();
|
||||||
for (AccountGroup.UUID includedGroupUuid : groupDetail.getIncludes()) {
|
if (recursive && groupControl.canSeeGroup()) {
|
||||||
if (!seenGroups.contains(includedGroupUuid)) {
|
for (AccountGroup.UUID subgroupUuid : group.getSubgroups()) {
|
||||||
members.putAll(getMembers(includedGroupUuid, seenGroups));
|
if (!seenGroups.contains(subgroupUuid)) {
|
||||||
|
indirectMembers.addAll(getMembers(subgroupUuid, seenGroups));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
accountLoader.fill();
|
return Sets.union(directMembers, indirectMembers);
|
||||||
return members;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,15 +35,15 @@ import java.util.List;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ListIncludedGroups implements RestReadView<GroupResource> {
|
public class ListSubgroups implements RestReadView<GroupResource> {
|
||||||
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ListIncludedGroups.class);
|
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ListSubgroups.class);
|
||||||
|
|
||||||
private final GroupControl.Factory controlFactory;
|
private final GroupControl.Factory controlFactory;
|
||||||
private final GroupIncludeCache groupIncludeCache;
|
private final GroupIncludeCache groupIncludeCache;
|
||||||
private final GroupJson json;
|
private final GroupJson json;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ListIncludedGroups(
|
ListSubgroups(
|
||||||
GroupControl.Factory controlFactory, GroupIncludeCache groupIncludeCache, GroupJson json) {
|
GroupControl.Factory controlFactory, GroupIncludeCache groupIncludeCache, GroupJson json) {
|
||||||
this.controlFactory = controlFactory;
|
this.controlFactory = controlFactory;
|
||||||
this.groupIncludeCache = groupIncludeCache;
|
this.groupIncludeCache = groupIncludeCache;
|
||||||
@@ -57,19 +57,18 @@ public class ListIncludedGroups implements RestReadView<GroupResource> {
|
|||||||
|
|
||||||
boolean ownerOfParent = rsrc.getControl().isOwner();
|
boolean ownerOfParent = rsrc.getControl().isOwner();
|
||||||
List<GroupInfo> included = new ArrayList<>();
|
List<GroupInfo> included = new ArrayList<>();
|
||||||
Collection<AccountGroup.UUID> includedGroupUuids =
|
Collection<AccountGroup.UUID> subgroupUuids =
|
||||||
groupIncludeCache.subgroupsOf(group.getGroupUUID());
|
groupIncludeCache.subgroupsOf(group.getGroupUUID());
|
||||||
for (AccountGroup.UUID includedGroupUuid : includedGroupUuids) {
|
for (AccountGroup.UUID subgroupUuid : subgroupUuids) {
|
||||||
try {
|
try {
|
||||||
GroupControl i = controlFactory.controlFor(includedGroupUuid);
|
GroupControl i = controlFactory.controlFor(subgroupUuid);
|
||||||
if (ownerOfParent || i.isVisible()) {
|
if (ownerOfParent || i.isVisible()) {
|
||||||
included.add(json.format(i.getGroup()));
|
included.add(json.format(i.getGroup()));
|
||||||
}
|
}
|
||||||
} catch (NoSuchGroupException notFound) {
|
} catch (NoSuchGroupException notFound) {
|
||||||
log.warn(
|
log.warn(
|
||||||
String.format(
|
String.format(
|
||||||
"Group %s no longer available, included into %s",
|
"Group %s no longer available, subgroup of %s", subgroupUuid, group.getName()));
|
||||||
includedGroupUuid, group.getName()));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,18 +15,18 @@
|
|||||||
package com.google.gerrit.server.group;
|
package com.google.gerrit.server.group;
|
||||||
|
|
||||||
import static com.google.gerrit.server.group.GroupResource.GROUP_KIND;
|
import static com.google.gerrit.server.group.GroupResource.GROUP_KIND;
|
||||||
import static com.google.gerrit.server.group.IncludedGroupResource.INCLUDED_GROUP_KIND;
|
|
||||||
import static com.google.gerrit.server.group.MemberResource.MEMBER_KIND;
|
import static com.google.gerrit.server.group.MemberResource.MEMBER_KIND;
|
||||||
|
import static com.google.gerrit.server.group.SubgroupResource.SUBGROUP_KIND;
|
||||||
|
|
||||||
import com.google.gerrit.audit.GroupMemberAuditListener;
|
import com.google.gerrit.audit.GroupMemberAuditListener;
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiModule;
|
import com.google.gerrit.extensions.restapi.RestApiModule;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups.UpdateIncludedGroup;
|
|
||||||
import com.google.gerrit.server.group.AddMembers.UpdateMember;
|
import com.google.gerrit.server.group.AddMembers.UpdateMember;
|
||||||
import com.google.gerrit.server.group.DeleteIncludedGroups.DeleteIncludedGroup;
|
import com.google.gerrit.server.group.AddSubgroups.UpdateSubgroup;
|
||||||
import com.google.gerrit.server.group.DeleteMembers.DeleteMember;
|
import com.google.gerrit.server.group.DeleteMembers.DeleteMember;
|
||||||
|
import com.google.gerrit.server.group.DeleteSubgroups.DeleteSubgroup;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
|
|
||||||
public class Module extends RestApiModule {
|
public class Module extends RestApiModule {
|
||||||
@@ -36,7 +36,7 @@ public class Module extends RestApiModule {
|
|||||||
|
|
||||||
DynamicMap.mapOf(binder(), GROUP_KIND);
|
DynamicMap.mapOf(binder(), GROUP_KIND);
|
||||||
DynamicMap.mapOf(binder(), MEMBER_KIND);
|
DynamicMap.mapOf(binder(), MEMBER_KIND);
|
||||||
DynamicMap.mapOf(binder(), INCLUDED_GROUP_KIND);
|
DynamicMap.mapOf(binder(), SUBGROUP_KIND);
|
||||||
|
|
||||||
get(GROUP_KIND).to(GetGroup.class);
|
get(GROUP_KIND).to(GetGroup.class);
|
||||||
put(GROUP_KIND).to(PutGroup.class);
|
put(GROUP_KIND).to(PutGroup.class);
|
||||||
@@ -45,9 +45,9 @@ public class Module extends RestApiModule {
|
|||||||
post(GROUP_KIND, "members").to(AddMembers.class);
|
post(GROUP_KIND, "members").to(AddMembers.class);
|
||||||
post(GROUP_KIND, "members.add").to(AddMembers.class);
|
post(GROUP_KIND, "members.add").to(AddMembers.class);
|
||||||
post(GROUP_KIND, "members.delete").to(DeleteMembers.class);
|
post(GROUP_KIND, "members.delete").to(DeleteMembers.class);
|
||||||
post(GROUP_KIND, "groups").to(AddIncludedGroups.class);
|
post(GROUP_KIND, "groups").to(AddSubgroups.class);
|
||||||
post(GROUP_KIND, "groups.add").to(AddIncludedGroups.class);
|
post(GROUP_KIND, "groups.add").to(AddSubgroups.class);
|
||||||
post(GROUP_KIND, "groups.delete").to(DeleteIncludedGroups.class);
|
post(GROUP_KIND, "groups.delete").to(DeleteSubgroups.class);
|
||||||
get(GROUP_KIND, "description").to(GetDescription.class);
|
get(GROUP_KIND, "description").to(GetDescription.class);
|
||||||
put(GROUP_KIND, "description").to(PutDescription.class);
|
put(GROUP_KIND, "description").to(PutDescription.class);
|
||||||
delete(GROUP_KIND, "description").to(PutDescription.class);
|
delete(GROUP_KIND, "description").to(PutDescription.class);
|
||||||
@@ -64,10 +64,10 @@ public class Module extends RestApiModule {
|
|||||||
put(MEMBER_KIND).to(UpdateMember.class);
|
put(MEMBER_KIND).to(UpdateMember.class);
|
||||||
delete(MEMBER_KIND).to(DeleteMember.class);
|
delete(MEMBER_KIND).to(DeleteMember.class);
|
||||||
|
|
||||||
child(GROUP_KIND, "groups").to(IncludedGroupsCollection.class);
|
child(GROUP_KIND, "groups").to(SubgroupsCollection.class);
|
||||||
get(INCLUDED_GROUP_KIND).to(GetIncludedGroup.class);
|
get(SUBGROUP_KIND).to(GetSubgroup.class);
|
||||||
put(INCLUDED_GROUP_KIND).to(UpdateIncludedGroup.class);
|
put(SUBGROUP_KIND).to(UpdateSubgroup.class);
|
||||||
delete(INCLUDED_GROUP_KIND).to(DeleteIncludedGroup.class);
|
delete(SUBGROUP_KIND).to(DeleteSubgroup.class);
|
||||||
|
|
||||||
factory(CreateGroup.Factory.class);
|
factory(CreateGroup.Factory.class);
|
||||||
factory(GroupsUpdate.Factory.class);
|
factory(GroupsUpdate.Factory.class);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ package com.google.gerrit.server.group;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.gerrit.common.data.GroupDescriptions;
|
|
||||||
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;
|
||||||
@@ -25,7 +24,6 @@ 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.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.index.query.QueryResult;
|
import com.google.gerrit.index.query.QueryResult;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import com.google.gerrit.server.index.group.GroupIndex;
|
import com.google.gerrit.server.index.group.GroupIndex;
|
||||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||||
import com.google.gerrit.server.query.group.GroupQueryBuilder;
|
import com.google.gerrit.server.query.group.GroupQueryBuilder;
|
||||||
@@ -123,13 +121,13 @@ public class QueryGroups implements RestReadView<TopLevelResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QueryResult<AccountGroup> result = queryProcessor.query(queryBuilder.parse(query));
|
QueryResult<InternalGroup> result = queryProcessor.query(queryBuilder.parse(query));
|
||||||
List<AccountGroup> groups = result.entities();
|
List<InternalGroup> groups = result.entities();
|
||||||
|
|
||||||
ArrayList<GroupInfo> groupInfos = Lists.newArrayListWithCapacity(groups.size());
|
ArrayList<GroupInfo> groupInfos = Lists.newArrayListWithCapacity(groups.size());
|
||||||
json.addOptions(options);
|
json.addOptions(options);
|
||||||
for (AccountGroup group : groups) {
|
for (InternalGroup group : groups) {
|
||||||
groupInfos.add(json.format(GroupDescriptions.forAccountGroup(group)));
|
groupInfos.add(json.format(new InternalGroupDescription(group)));
|
||||||
}
|
}
|
||||||
if (!groupInfos.isEmpty() && result.more()) {
|
if (!groupInfos.isEmpty() && result.more()) {
|
||||||
groupInfos.get(groupInfos.size() - 1)._moreGroups = true;
|
groupInfos.get(groupInfos.size() - 1)._moreGroups = true;
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ import com.google.gerrit.extensions.restapi.RestView;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
public class IncludedGroupResource extends GroupResource {
|
public class SubgroupResource extends GroupResource {
|
||||||
public static final TypeLiteral<RestView<IncludedGroupResource>> INCLUDED_GROUP_KIND =
|
public static final TypeLiteral<RestView<SubgroupResource>> SUBGROUP_KIND =
|
||||||
new TypeLiteral<RestView<IncludedGroupResource>>() {};
|
new TypeLiteral<RestView<SubgroupResource>>() {};
|
||||||
|
|
||||||
private final GroupDescription.Basic member;
|
private final GroupDescription.Basic member;
|
||||||
|
|
||||||
public IncludedGroupResource(GroupResource group, GroupDescription.Basic member) {
|
public SubgroupResource(GroupResource group, GroupDescription.Basic member) {
|
||||||
super(group);
|
super(group);
|
||||||
this.member = member;
|
this.member = member;
|
||||||
}
|
}
|
||||||
@@ -26,36 +26,36 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
|||||||
import com.google.gerrit.extensions.restapi.RestView;
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups.PutIncludedGroup;
|
import com.google.gerrit.server.group.AddSubgroups.PutSubgroup;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class IncludedGroupsCollection
|
public class SubgroupsCollection
|
||||||
implements ChildCollection<GroupResource, IncludedGroupResource>, AcceptsCreate<GroupResource> {
|
implements ChildCollection<GroupResource, SubgroupResource>, AcceptsCreate<GroupResource> {
|
||||||
private final DynamicMap<RestView<IncludedGroupResource>> views;
|
private final DynamicMap<RestView<SubgroupResource>> views;
|
||||||
private final ListIncludedGroups list;
|
private final ListSubgroups list;
|
||||||
private final GroupsCollection groupsCollection;
|
private final GroupsCollection groupsCollection;
|
||||||
private final Provider<ReviewDb> dbProvider;
|
private final Provider<ReviewDb> dbProvider;
|
||||||
private final Groups groups;
|
private final Groups groups;
|
||||||
private final AddIncludedGroups put;
|
private final AddSubgroups addSubgroups;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
IncludedGroupsCollection(
|
SubgroupsCollection(
|
||||||
DynamicMap<RestView<IncludedGroupResource>> views,
|
DynamicMap<RestView<SubgroupResource>> views,
|
||||||
ListIncludedGroups list,
|
ListSubgroups list,
|
||||||
GroupsCollection groupsCollection,
|
GroupsCollection groupsCollection,
|
||||||
Provider<ReviewDb> dbProvider,
|
Provider<ReviewDb> dbProvider,
|
||||||
Groups groups,
|
Groups groups,
|
||||||
AddIncludedGroups put) {
|
AddSubgroups addSubgroups) {
|
||||||
this.views = views;
|
this.views = views;
|
||||||
this.list = list;
|
this.list = list;
|
||||||
this.groupsCollection = groupsCollection;
|
this.groupsCollection = groupsCollection;
|
||||||
this.dbProvider = dbProvider;
|
this.dbProvider = dbProvider;
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
this.put = put;
|
this.addSubgroups = addSubgroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,23 +64,23 @@ public class IncludedGroupsCollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IncludedGroupResource parse(GroupResource resource, IdString id)
|
public SubgroupResource parse(GroupResource resource, IdString id)
|
||||||
throws MethodNotAllowedException, AuthException, ResourceNotFoundException, OrmException {
|
throws MethodNotAllowedException, AuthException, ResourceNotFoundException, OrmException {
|
||||||
GroupDescription.Internal parent =
|
GroupDescription.Internal parent =
|
||||||
resource.asInternalGroup().orElseThrow(MethodNotAllowedException::new);
|
resource.asInternalGroup().orElseThrow(MethodNotAllowedException::new);
|
||||||
|
|
||||||
GroupDescription.Basic member =
|
GroupDescription.Basic member =
|
||||||
groupsCollection.parse(TopLevelResource.INSTANCE, id).getGroup();
|
groupsCollection.parse(TopLevelResource.INSTANCE, id).getGroup();
|
||||||
if (resource.getControl().canSeeGroup() && isMember(parent, member)) {
|
if (resource.getControl().canSeeGroup() && isSubgroup(parent, member)) {
|
||||||
return new IncludedGroupResource(resource, member);
|
return new SubgroupResource(resource, member);
|
||||||
}
|
}
|
||||||
throw new ResourceNotFoundException(id);
|
throw new ResourceNotFoundException(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMember(GroupDescription.Internal parent, GroupDescription.Basic member)
|
private boolean isSubgroup(GroupDescription.Internal parent, GroupDescription.Basic member)
|
||||||
throws OrmException, ResourceNotFoundException {
|
throws OrmException, ResourceNotFoundException {
|
||||||
try {
|
try {
|
||||||
return groups.isIncluded(dbProvider.get(), parent.getGroupUUID(), member.getGroupUUID());
|
return groups.isSubgroup(dbProvider.get(), parent.getGroupUUID(), member.getGroupUUID());
|
||||||
} catch (NoSuchGroupException e) {
|
} catch (NoSuchGroupException e) {
|
||||||
throw new ResourceNotFoundException(
|
throw new ResourceNotFoundException(
|
||||||
String.format("Group %s not found", parent.getGroupUUID()));
|
String.format("Group %s not found", parent.getGroupUUID()));
|
||||||
@@ -89,12 +89,12 @@ public class IncludedGroupsCollection
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public PutIncludedGroup create(GroupResource group, IdString id) {
|
public PutSubgroup create(GroupResource group, IdString id) {
|
||||||
return new PutIncludedGroup(put, id.get());
|
return new PutSubgroup(addSubgroups, id.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DynamicMap<RestView<IncludedGroupResource>> views() {
|
public DynamicMap<RestView<SubgroupResource>> views() {
|
||||||
return views;
|
return views;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,8 @@ package com.google.gerrit.server.index;
|
|||||||
import com.google.gerrit.index.Index;
|
import com.google.gerrit.index.Index;
|
||||||
import com.google.gerrit.index.IndexConfig;
|
import com.google.gerrit.index.IndexConfig;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.account.AccountIndex;
|
import com.google.gerrit.server.index.account.AccountIndex;
|
||||||
import com.google.gerrit.server.index.change.ChangeIndex;
|
import com.google.gerrit.server.index.change.ChangeIndex;
|
||||||
import com.google.gerrit.server.index.change.DummyChangeIndex;
|
import com.google.gerrit.server.index.change.DummyChangeIndex;
|
||||||
@@ -43,7 +43,7 @@ public class DummyIndexModule extends AbstractModule {
|
|||||||
|
|
||||||
private static class DummyGroupIndexFactory implements GroupIndex.Factory {
|
private static class DummyGroupIndexFactory implements GroupIndex.Factory {
|
||||||
@Override
|
@Override
|
||||||
public GroupIndex create(Schema<AccountGroup> schema) {
|
public GroupIndex create(Schema<InternalGroup> schema) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
|||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.group.Groups;
|
import com.google.gerrit.server.group.Groups;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.IndexExecutor;
|
import com.google.gerrit.server.index.IndexExecutor;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
@@ -34,6 +35,7 @@ import com.google.inject.Singleton;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@@ -43,7 +45,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AllGroupsIndexer extends SiteIndexer<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
public class AllGroupsIndexer extends SiteIndexer<AccountGroup.UUID, InternalGroup, GroupIndex> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(AllGroupsIndexer.class);
|
private static final Logger log = LoggerFactory.getLogger(AllGroupsIndexer.class);
|
||||||
|
|
||||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||||
@@ -92,11 +94,17 @@ public class AllGroupsIndexer extends SiteIndexer<AccountGroup.UUID, AccountGrou
|
|||||||
executor.submit(
|
executor.submit(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
AccountGroup oldGroup = groupCache.get(uuid);
|
Optional<InternalGroup> oldGroup = groupCache.get(uuid);
|
||||||
if (oldGroup != null) {
|
if (oldGroup.isPresent()) {
|
||||||
groupCache.evict(oldGroup);
|
InternalGroup group = oldGroup.get();
|
||||||
|
groupCache.evict(group.getGroupUUID(), group.getId(), group.getNameKey());
|
||||||
|
}
|
||||||
|
Optional<InternalGroup> internalGroup = groupCache.get(uuid);
|
||||||
|
if (internalGroup.isPresent()) {
|
||||||
|
index.replace(internalGroup.get());
|
||||||
|
} else {
|
||||||
|
index.delete(uuid);
|
||||||
}
|
}
|
||||||
index.replace(groupCache.get(uuid));
|
|
||||||
verboseWriter.println("Reindexed " + desc);
|
verboseWriter.println("Reindexed " + desc);
|
||||||
done.incrementAndGet();
|
done.incrementAndGet();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.index.group;
|
package com.google.gerrit.server.index.group;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static com.google.gerrit.index.FieldDef.exact;
|
import static com.google.gerrit.index.FieldDef.exact;
|
||||||
import static com.google.gerrit.index.FieldDef.fullText;
|
import static com.google.gerrit.index.FieldDef.fullText;
|
||||||
import static com.google.gerrit.index.FieldDef.integer;
|
import static com.google.gerrit.index.FieldDef.integer;
|
||||||
@@ -22,40 +23,53 @@ import static com.google.gerrit.index.FieldDef.timestamp;
|
|||||||
|
|
||||||
import com.google.gerrit.index.FieldDef;
|
import com.google.gerrit.index.FieldDef;
|
||||||
import com.google.gerrit.index.SchemaUtil;
|
import com.google.gerrit.index.SchemaUtil;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
/** Secondary index schemas for groups. */
|
/** Secondary index schemas for groups. */
|
||||||
public class GroupField {
|
public class GroupField {
|
||||||
/** Legacy group ID. */
|
/** Legacy group ID. */
|
||||||
public static final FieldDef<AccountGroup, Integer> ID =
|
public static final FieldDef<InternalGroup, Integer> ID =
|
||||||
integer("id").build(g -> g.getId().get());
|
integer("id").build(g -> g.getId().get());
|
||||||
|
|
||||||
/** Group UUID. */
|
/** Group UUID. */
|
||||||
public static final FieldDef<AccountGroup, String> UUID =
|
public static final FieldDef<InternalGroup, String> UUID =
|
||||||
exact("uuid").stored().build(g -> g.getGroupUUID().get());
|
exact("uuid").stored().build(g -> g.getGroupUUID().get());
|
||||||
|
|
||||||
/** Group owner UUID. */
|
/** Group owner UUID. */
|
||||||
public static final FieldDef<AccountGroup, String> OWNER_UUID =
|
public static final FieldDef<InternalGroup, String> OWNER_UUID =
|
||||||
exact("owner_uuid").build(g -> g.getOwnerGroupUUID().get());
|
exact("owner_uuid").build(g -> g.getOwnerGroupUUID().get());
|
||||||
|
|
||||||
/** Timestamp indicating when this group was created. */
|
/** Timestamp indicating when this group was created. */
|
||||||
public static final FieldDef<AccountGroup, Timestamp> CREATED_ON =
|
public static final FieldDef<InternalGroup, Timestamp> CREATED_ON =
|
||||||
timestamp("created_on").build(AccountGroup::getCreatedOn);
|
timestamp("created_on").build(InternalGroup::getCreatedOn);
|
||||||
|
|
||||||
/** Group name. */
|
/** Group name. */
|
||||||
public static final FieldDef<AccountGroup, String> NAME =
|
public static final FieldDef<InternalGroup, String> NAME =
|
||||||
exact("name").build(AccountGroup::getName);
|
exact("name").build(InternalGroup::getName);
|
||||||
|
|
||||||
/** Prefix match on group name parts. */
|
/** Prefix match on group name parts. */
|
||||||
public static final FieldDef<AccountGroup, Iterable<String>> NAME_PART =
|
public static final FieldDef<InternalGroup, Iterable<String>> NAME_PART =
|
||||||
prefix("name_part").buildRepeatable(g -> SchemaUtil.getNameParts(g.getName()));
|
prefix("name_part").buildRepeatable(g -> SchemaUtil.getNameParts(g.getName()));
|
||||||
|
|
||||||
/** Group description. */
|
/** Group description. */
|
||||||
public static final FieldDef<AccountGroup, String> DESCRIPTION =
|
public static final FieldDef<InternalGroup, String> DESCRIPTION =
|
||||||
fullText("description").build(AccountGroup::getDescription);
|
fullText("description").build(InternalGroup::getDescription);
|
||||||
|
|
||||||
/** Whether the group is visible to all users. */
|
/** Whether the group is visible to all users. */
|
||||||
public static final FieldDef<AccountGroup, String> IS_VISIBLE_TO_ALL =
|
public static final FieldDef<InternalGroup, String> IS_VISIBLE_TO_ALL =
|
||||||
exact("is_visible_to_all").build(g -> g.isVisibleToAll() ? "1" : "0");
|
exact("is_visible_to_all").build(g -> g.isVisibleToAll() ? "1" : "0");
|
||||||
|
|
||||||
|
public static final FieldDef<InternalGroup, Iterable<Integer>> MEMBER =
|
||||||
|
integer("member")
|
||||||
|
.buildRepeatable(
|
||||||
|
g -> g.getMembers().stream().map(Account.Id::get).collect(toImmutableList()));
|
||||||
|
|
||||||
|
public static final FieldDef<InternalGroup, Iterable<String>> SUBGROUP =
|
||||||
|
exact("subgroup")
|
||||||
|
.buildRepeatable(
|
||||||
|
g ->
|
||||||
|
g.getSubgroups().stream().map(AccountGroup.UUID::get).collect(toImmutableList()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,15 @@ import com.google.gerrit.index.Index;
|
|||||||
import com.google.gerrit.index.IndexDefinition;
|
import com.google.gerrit.index.IndexDefinition;
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.query.group.GroupPredicates;
|
import com.google.gerrit.server.query.group.GroupPredicates;
|
||||||
|
|
||||||
public interface GroupIndex extends Index<AccountGroup.UUID, AccountGroup> {
|
public interface GroupIndex extends Index<AccountGroup.UUID, InternalGroup> {
|
||||||
public interface Factory
|
public interface Factory
|
||||||
extends IndexDefinition.IndexFactory<AccountGroup.UUID, AccountGroup, GroupIndex> {}
|
extends IndexDefinition.IndexFactory<AccountGroup.UUID, InternalGroup, GroupIndex> {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default Predicate<AccountGroup> keyPredicate(AccountGroup.UUID uuid) {
|
default Predicate<InternalGroup> keyPredicate(AccountGroup.UUID uuid) {
|
||||||
return GroupPredicates.uuid(uuid);
|
return GroupPredicates.uuid(uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ package com.google.gerrit.server.index.group;
|
|||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.gerrit.index.IndexCollection;
|
import com.google.gerrit.index.IndexCollection;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class GroupIndexCollection
|
public class GroupIndexCollection
|
||||||
extends IndexCollection<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
extends IndexCollection<AccountGroup.UUID, InternalGroup, GroupIndex> {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public GroupIndexCollection() {}
|
public GroupIndexCollection() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ package com.google.gerrit.server.index.group;
|
|||||||
import com.google.gerrit.common.Nullable;
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.index.IndexDefinition;
|
import com.google.gerrit.index.IndexDefinition;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
public class GroupIndexDefinition
|
public class GroupIndexDefinition
|
||||||
extends IndexDefinition<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
extends IndexDefinition<AccountGroup.UUID, InternalGroup, GroupIndex> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GroupIndexDefinition(
|
GroupIndexDefinition(
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ import com.google.gerrit.index.IndexRewriter;
|
|||||||
import com.google.gerrit.index.QueryOptions;
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class GroupIndexRewriter implements IndexRewriter<AccountGroup> {
|
public class GroupIndexRewriter implements IndexRewriter<InternalGroup> {
|
||||||
private final GroupIndexCollection indexes;
|
private final GroupIndexCollection indexes;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -34,7 +34,7 @@ public class GroupIndexRewriter implements IndexRewriter<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Predicate<AccountGroup> rewrite(Predicate<AccountGroup> in, QueryOptions opts)
|
public Predicate<InternalGroup> rewrite(Predicate<InternalGroup> in, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
GroupIndex index = indexes.getSearchIndex();
|
GroupIndex index = indexes.getSearchIndex();
|
||||||
checkNotNull(index, "no active search index configured for groups");
|
checkNotNull(index, "no active search index configured for groups");
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ import com.google.gerrit.extensions.registration.DynamicSet;
|
|||||||
import com.google.gerrit.index.Index;
|
import com.google.gerrit.index.Index;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import com.google.inject.assistedinject.AssistedInject;
|
import com.google.inject.assistedinject.AssistedInject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class GroupIndexerImpl implements GroupIndexer {
|
public class GroupIndexerImpl implements GroupIndexer {
|
||||||
public interface Factory {
|
public interface Factory {
|
||||||
@@ -63,8 +65,13 @@ public class GroupIndexerImpl implements GroupIndexer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void index(AccountGroup.UUID uuid) throws IOException {
|
public void index(AccountGroup.UUID uuid) throws IOException {
|
||||||
for (Index<?, AccountGroup> i : getWriteIndexes()) {
|
for (Index<AccountGroup.UUID, InternalGroup> i : getWriteIndexes()) {
|
||||||
i.replace(groupCache.get(uuid));
|
Optional<InternalGroup> internalGroup = groupCache.get(uuid);
|
||||||
|
if (internalGroup.isPresent()) {
|
||||||
|
i.replace(internalGroup.get());
|
||||||
|
} else {
|
||||||
|
i.delete(uuid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fireGroupIndexedEvent(uuid.get());
|
fireGroupIndexedEvent(uuid.get());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import static com.google.gerrit.index.SchemaUtil.schema;
|
|||||||
|
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.index.SchemaDefinitions;
|
import com.google.gerrit.index.SchemaDefinitions;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
|
||||||
public class GroupSchemaDefinitions extends SchemaDefinitions<AccountGroup> {
|
public class GroupSchemaDefinitions extends SchemaDefinitions<InternalGroup> {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
static final Schema<AccountGroup> V2 =
|
static final Schema<InternalGroup> V2 =
|
||||||
schema(
|
schema(
|
||||||
GroupField.DESCRIPTION,
|
GroupField.DESCRIPTION,
|
||||||
GroupField.ID,
|
GroupField.ID,
|
||||||
@@ -32,11 +32,13 @@ public class GroupSchemaDefinitions extends SchemaDefinitions<AccountGroup> {
|
|||||||
GroupField.OWNER_UUID,
|
GroupField.OWNER_UUID,
|
||||||
GroupField.UUID);
|
GroupField.UUID);
|
||||||
|
|
||||||
static final Schema<AccountGroup> V3 = schema(V2, GroupField.CREATED_ON);
|
@Deprecated static final Schema<InternalGroup> V3 = schema(V2, GroupField.CREATED_ON);
|
||||||
|
|
||||||
|
static final Schema<InternalGroup> V4 = schema(V3, GroupField.MEMBER, GroupField.SUBGROUP);
|
||||||
|
|
||||||
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
|
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
|
||||||
|
|
||||||
private GroupSchemaDefinitions() {
|
private GroupSchemaDefinitions() {
|
||||||
super("groups", AccountGroup.class);
|
super("groups", InternalGroup.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,15 @@ import com.google.gerrit.index.query.DataSource;
|
|||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
|
||||||
public class IndexedGroupQuery extends IndexedQuery<AccountGroup.UUID, AccountGroup>
|
public class IndexedGroupQuery extends IndexedQuery<AccountGroup.UUID, InternalGroup>
|
||||||
implements DataSource<AccountGroup> {
|
implements DataSource<InternalGroup> {
|
||||||
|
|
||||||
public IndexedGroupQuery(
|
public IndexedGroupQuery(
|
||||||
Index<AccountGroup.UUID, AccountGroup> index, Predicate<AccountGroup> pred, QueryOptions opts)
|
Index<AccountGroup.UUID, InternalGroup> index,
|
||||||
|
Predicate<InternalGroup> pred,
|
||||||
|
QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
super(index, pred, opts.convertForBackend());
|
super(index, pred, opts.convertForBackend());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ package com.google.gerrit.server.query.group;
|
|||||||
|
|
||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||||
import com.google.gerrit.index.query.IsVisibleToPredicate;
|
import com.google.gerrit.index.query.IsVisibleToPredicate;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.gerrit.server.query.account.AccountQueryBuilder;
|
import com.google.gerrit.server.query.account.AccountQueryBuilder;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
public class GroupIsVisibleToPredicate extends IsVisibleToPredicate<AccountGroup> {
|
public class GroupIsVisibleToPredicate extends IsVisibleToPredicate<InternalGroup> {
|
||||||
protected final GroupControl.GenericFactory groupControlFactory;
|
protected final GroupControl.GenericFactory groupControlFactory;
|
||||||
protected final CurrentUser user;
|
protected final CurrentUser user;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ public class GroupIsVisibleToPredicate extends IsVisibleToPredicate<AccountGroup
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean match(AccountGroup group) throws OrmException {
|
public boolean match(InternalGroup group) throws OrmException {
|
||||||
try {
|
try {
|
||||||
return groupControlFactory.controlFor(user, group.getGroupUUID()).isVisible();
|
return groupControlFactory.controlFor(user, group.getGroupUUID()).isVisible();
|
||||||
} catch (NoSuchGroupException e) {
|
} catch (NoSuchGroupException e) {
|
||||||
|
|||||||
@@ -17,44 +17,54 @@ package com.google.gerrit.server.query.group;
|
|||||||
import com.google.gerrit.index.FieldDef;
|
import com.google.gerrit.index.FieldDef;
|
||||||
import com.google.gerrit.index.query.IndexPredicate;
|
import com.google.gerrit.index.query.IndexPredicate;
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.group.GroupField;
|
import com.google.gerrit.server.index.group.GroupField;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class GroupPredicates {
|
public class GroupPredicates {
|
||||||
public static Predicate<AccountGroup> uuid(AccountGroup.UUID uuid) {
|
public static Predicate<InternalGroup> uuid(AccountGroup.UUID uuid) {
|
||||||
return new GroupPredicate(GroupField.UUID, GroupQueryBuilder.FIELD_UUID, uuid.get());
|
return new GroupPredicate(GroupField.UUID, GroupQueryBuilder.FIELD_UUID, uuid.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Predicate<AccountGroup> description(String description) {
|
public static Predicate<InternalGroup> description(String description) {
|
||||||
return new GroupPredicate(
|
return new GroupPredicate(
|
||||||
GroupField.DESCRIPTION, GroupQueryBuilder.FIELD_DESCRIPTION, description);
|
GroupField.DESCRIPTION, GroupQueryBuilder.FIELD_DESCRIPTION, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Predicate<AccountGroup> inname(String name) {
|
public static Predicate<InternalGroup> inname(String name) {
|
||||||
return new GroupPredicate(
|
return new GroupPredicate(
|
||||||
GroupField.NAME_PART, GroupQueryBuilder.FIELD_INNAME, name.toLowerCase(Locale.US));
|
GroupField.NAME_PART, GroupQueryBuilder.FIELD_INNAME, name.toLowerCase(Locale.US));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Predicate<AccountGroup> name(String name) {
|
public static Predicate<InternalGroup> name(String name) {
|
||||||
return new GroupPredicate(GroupField.NAME, GroupQueryBuilder.FIELD_NAME, name);
|
return new GroupPredicate(GroupField.NAME, GroupQueryBuilder.FIELD_NAME, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Predicate<AccountGroup> owner(AccountGroup.UUID ownerUuid) {
|
public static Predicate<InternalGroup> owner(AccountGroup.UUID ownerUuid) {
|
||||||
return new GroupPredicate(
|
return new GroupPredicate(
|
||||||
GroupField.OWNER_UUID, GroupQueryBuilder.FIELD_OWNER, ownerUuid.get());
|
GroupField.OWNER_UUID, GroupQueryBuilder.FIELD_OWNER, ownerUuid.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Predicate<AccountGroup> isVisibleToAll() {
|
public static Predicate<InternalGroup> isVisibleToAll() {
|
||||||
return new GroupPredicate(GroupField.IS_VISIBLE_TO_ALL, "1");
|
return new GroupPredicate(GroupField.IS_VISIBLE_TO_ALL, "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
static class GroupPredicate extends IndexPredicate<AccountGroup> {
|
public static Predicate<InternalGroup> member(Account.Id memberId) {
|
||||||
GroupPredicate(FieldDef<AccountGroup, ?> def, String value) {
|
return new GroupPredicate(GroupField.MEMBER, memberId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<InternalGroup> subgroup(AccountGroup.UUID subgroupUuid) {
|
||||||
|
return new GroupPredicate(GroupField.SUBGROUP, subgroupUuid.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class GroupPredicate extends IndexPredicate<InternalGroup> {
|
||||||
|
GroupPredicate(FieldDef<InternalGroup, ?> def, String value) {
|
||||||
super(def, value);
|
super(def, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupPredicate(FieldDef<AccountGroup, ?> def, String name, String value) {
|
GroupPredicate(FieldDef<InternalGroup, ?> def, String name, String value) {
|
||||||
super(def, name, value);
|
super(def, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,23 +14,39 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.query.group;
|
package com.google.gerrit.server.query.group;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.gerrit.common.data.GroupReference;
|
import com.google.gerrit.common.data.GroupReference;
|
||||||
|
import com.google.gerrit.index.FieldDef;
|
||||||
import com.google.gerrit.index.query.LimitPredicate;
|
import com.google.gerrit.index.query.LimitPredicate;
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryBuilder;
|
import com.google.gerrit.index.query.QueryBuilder;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
|
import com.google.gerrit.server.account.AccountResolver;
|
||||||
import com.google.gerrit.server.account.GroupBackend;
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
import com.google.gerrit.server.account.GroupBackends;
|
import com.google.gerrit.server.account.GroupBackends;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
import com.google.gerrit.server.index.group.GroupField;
|
||||||
|
import com.google.gerrit.server.index.group.GroupIndex;
|
||||||
|
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
/** Parses a query string meant to be applied to group objects. */
|
/** Parses a query string meant to be applied to group objects. */
|
||||||
public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
|
||||||
public static final String FIELD_UUID = "uuid";
|
public static final String FIELD_UUID = "uuid";
|
||||||
public static final String FIELD_DESCRIPTION = "description";
|
public static final String FIELD_DESCRIPTION = "description";
|
||||||
public static final String FIELD_INNAME = "inname";
|
public static final String FIELD_INNAME = "inname";
|
||||||
@@ -38,17 +54,28 @@ public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
|||||||
public static final String FIELD_OWNER = "owner";
|
public static final String FIELD_OWNER = "owner";
|
||||||
public static final String FIELD_LIMIT = "limit";
|
public static final String FIELD_LIMIT = "limit";
|
||||||
|
|
||||||
private static final QueryBuilder.Definition<AccountGroup, GroupQueryBuilder> mydef =
|
private static final QueryBuilder.Definition<InternalGroup, GroupQueryBuilder> mydef =
|
||||||
new QueryBuilder.Definition<>(GroupQueryBuilder.class);
|
new QueryBuilder.Definition<>(GroupQueryBuilder.class);
|
||||||
|
|
||||||
public static class Arguments {
|
public static class Arguments {
|
||||||
|
final Provider<ReviewDb> db;
|
||||||
|
final GroupIndex groupIndex;
|
||||||
final GroupCache groupCache;
|
final GroupCache groupCache;
|
||||||
final GroupBackend groupBackend;
|
final GroupBackend groupBackend;
|
||||||
|
final AccountResolver accountResolver;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Arguments(GroupCache groupCache, GroupBackend groupBackend) {
|
Arguments(
|
||||||
|
Provider<ReviewDb> db,
|
||||||
|
GroupIndexCollection groupIndexCollection,
|
||||||
|
GroupCache groupCache,
|
||||||
|
GroupBackend groupBackend,
|
||||||
|
AccountResolver accountResolver) {
|
||||||
|
this.db = db;
|
||||||
|
this.groupIndex = groupIndexCollection.getSearchIndex();
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.groupBackend = groupBackend;
|
this.groupBackend = groupBackend;
|
||||||
|
this.accountResolver = accountResolver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,12 +88,12 @@ public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> uuid(String uuid) {
|
public Predicate<InternalGroup> uuid(String uuid) {
|
||||||
return GroupPredicates.uuid(new AccountGroup.UUID(uuid));
|
return GroupPredicates.uuid(new AccountGroup.UUID(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> description(String description) throws QueryParseException {
|
public Predicate<InternalGroup> description(String description) throws QueryParseException {
|
||||||
if (Strings.isNullOrEmpty(description)) {
|
if (Strings.isNullOrEmpty(description)) {
|
||||||
throw error("description operator requires a value");
|
throw error("description operator requires a value");
|
||||||
}
|
}
|
||||||
@@ -75,7 +102,7 @@ public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> inname(String namePart) {
|
public Predicate<InternalGroup> inname(String namePart) {
|
||||||
if (namePart.isEmpty()) {
|
if (namePart.isEmpty()) {
|
||||||
return name(namePart);
|
return name(namePart);
|
||||||
}
|
}
|
||||||
@@ -83,25 +110,18 @@ public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> name(String name) {
|
public Predicate<InternalGroup> name(String name) {
|
||||||
return GroupPredicates.name(name);
|
return GroupPredicates.name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> owner(String owner) throws QueryParseException {
|
public Predicate<InternalGroup> owner(String owner) throws QueryParseException {
|
||||||
AccountGroup group = args.groupCache.get(new AccountGroup.UUID(owner));
|
AccountGroup.UUID groupUuid = parseGroup(owner);
|
||||||
if (group != null) {
|
return GroupPredicates.owner(groupUuid);
|
||||||
return GroupPredicates.owner(group.getGroupUUID());
|
|
||||||
}
|
|
||||||
GroupReference g = GroupBackends.findBestSuggestion(args.groupBackend, owner);
|
|
||||||
if (g == null) {
|
|
||||||
throw error("Group " + owner + " not found");
|
|
||||||
}
|
|
||||||
return GroupPredicates.owner(g.getUUID());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> is(String value) throws QueryParseException {
|
public Predicate<InternalGroup> is(String value) throws QueryParseException {
|
||||||
if ("visibletoall".equalsIgnoreCase(value)) {
|
if ("visibletoall".equalsIgnoreCase(value)) {
|
||||||
return GroupPredicates.isVisibleToAll();
|
return GroupPredicates.isVisibleToAll();
|
||||||
}
|
}
|
||||||
@@ -109,9 +129,9 @@ public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Predicate<AccountGroup> defaultField(String query) throws QueryParseException {
|
protected Predicate<InternalGroup> defaultField(String query) throws QueryParseException {
|
||||||
// Adapt the capacity of this list when adding more default predicates.
|
// Adapt the capacity of this list when adding more default predicates.
|
||||||
List<Predicate<AccountGroup>> preds = Lists.newArrayListWithCapacity(5);
|
List<Predicate<InternalGroup>> preds = Lists.newArrayListWithCapacity(5);
|
||||||
preds.add(uuid(query));
|
preds.add(uuid(query));
|
||||||
preds.add(name(query));
|
preds.add(name(query));
|
||||||
preds.add(inname(query));
|
preds.add(inname(query));
|
||||||
@@ -127,11 +147,65 @@ public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<AccountGroup> limit(String query) throws QueryParseException {
|
public Predicate<InternalGroup> member(String query)
|
||||||
|
throws QueryParseException, OrmException, ConfigInvalidException, IOException {
|
||||||
|
if (isFieldAbsentFromIndex(GroupField.MEMBER)) {
|
||||||
|
throw getExceptionForUnsupportedOperator("member");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Account.Id> accounts = parseAccount(query);
|
||||||
|
List<Predicate<InternalGroup>> predicates =
|
||||||
|
accounts.stream().map(GroupPredicates::member).collect(toImmutableList());
|
||||||
|
return Predicate.or(predicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operator
|
||||||
|
public Predicate<InternalGroup> subgroup(String query) throws QueryParseException {
|
||||||
|
if (isFieldAbsentFromIndex(GroupField.SUBGROUP)) {
|
||||||
|
throw getExceptionForUnsupportedOperator("subgroup");
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountGroup.UUID groupUuid = parseGroup(query);
|
||||||
|
return GroupPredicates.subgroup(groupUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operator
|
||||||
|
public Predicate<InternalGroup> limit(String query) throws QueryParseException {
|
||||||
Integer limit = Ints.tryParse(query);
|
Integer limit = Ints.tryParse(query);
|
||||||
if (limit == null) {
|
if (limit == null) {
|
||||||
throw error("Invalid limit: " + query);
|
throw error("Invalid limit: " + query);
|
||||||
}
|
}
|
||||||
return new LimitPredicate<>(FIELD_LIMIT, limit);
|
return new LimitPredicate<>(FIELD_LIMIT, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isFieldAbsentFromIndex(FieldDef<InternalGroup, ?> field) {
|
||||||
|
return !args.groupIndex.getSchema().hasField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QueryParseException getExceptionForUnsupportedOperator(String operatorName) {
|
||||||
|
return new QueryParseException(
|
||||||
|
String.format("'%s' operator is not supported by group index version", operatorName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Account.Id> parseAccount(String nameOrEmail)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
|
Set<Account.Id> foundAccounts = args.accountResolver.findAll(args.db.get(), nameOrEmail);
|
||||||
|
if (foundAccounts.isEmpty()) {
|
||||||
|
throw error("User " + nameOrEmail + " not found");
|
||||||
|
}
|
||||||
|
return foundAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccountGroup.UUID parseGroup(String groupNameOrUuid) throws QueryParseException {
|
||||||
|
Optional<InternalGroup> group = args.groupCache.get(new AccountGroup.UUID(groupNameOrUuid));
|
||||||
|
if (group.isPresent()) {
|
||||||
|
return group.get().getGroupUUID();
|
||||||
|
}
|
||||||
|
GroupReference groupReference =
|
||||||
|
GroupBackends.findBestSuggestion(args.groupBackend, groupNameOrUuid);
|
||||||
|
if (groupReference == null) {
|
||||||
|
throw error("Group " + groupNameOrUuid + " not found");
|
||||||
|
}
|
||||||
|
return groupReference.getUUID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ import com.google.gerrit.index.query.IndexPredicate;
|
|||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryProcessor;
|
import com.google.gerrit.index.query.QueryProcessor;
|
||||||
import com.google.gerrit.metrics.MetricMaker;
|
import com.google.gerrit.metrics.MetricMaker;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.AccountLimits;
|
import com.google.gerrit.server.account.AccountLimits;
|
||||||
import com.google.gerrit.server.account.GroupControl;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||||
import com.google.gerrit.server.index.group.GroupIndexRewriter;
|
import com.google.gerrit.server.index.group.GroupIndexRewriter;
|
||||||
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
|
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
|
||||||
@@ -39,7 +39,7 @@ import com.google.inject.Provider;
|
|||||||
* <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
|
* <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
|
||||||
* holding on to a single instance.
|
* holding on to a single instance.
|
||||||
*/
|
*/
|
||||||
public class GroupQueryProcessor extends QueryProcessor<AccountGroup> {
|
public class GroupQueryProcessor extends QueryProcessor<InternalGroup> {
|
||||||
private final Provider<CurrentUser> userProvider;
|
private final Provider<CurrentUser> userProvider;
|
||||||
private final GroupControl.GenericFactory groupControlFactory;
|
private final GroupControl.GenericFactory groupControlFactory;
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public class GroupQueryProcessor extends QueryProcessor<AccountGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Predicate<AccountGroup> enforceVisibility(Predicate<AccountGroup> pred) {
|
protected Predicate<InternalGroup> enforceVisibility(Predicate<InternalGroup> pred) {
|
||||||
return new AndSource<>(
|
return new AndSource<>(
|
||||||
pred, new GroupIsVisibleToPredicate(groupControlFactory, userProvider.get()), start);
|
pred, new GroupIsVisibleToPredicate(groupControlFactory, userProvider.get()), start);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.schema;
|
package com.google.gerrit.server.schema;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
import com.google.gerrit.common.TimeUtil;
|
||||||
import com.google.gerrit.common.data.GroupReference;
|
import com.google.gerrit.common.data.GroupReference;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
@@ -25,6 +26,7 @@ import com.google.gerrit.server.account.GroupUUID;
|
|||||||
import com.google.gerrit.server.config.SitePath;
|
import com.google.gerrit.server.config.SitePath;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
import com.google.gerrit.server.group.GroupsUpdate;
|
import com.google.gerrit.server.group.GroupsUpdate;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.index.group.GroupIndex;
|
import com.google.gerrit.server.index.group.GroupIndex;
|
||||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||||
import com.google.gwtorm.jdbc.JdbcExecutor;
|
import com.google.gwtorm.jdbc.JdbcExecutor;
|
||||||
@@ -100,16 +102,16 @@ public class SchemaCreator {
|
|||||||
admin = newGroup(db, "Administrators");
|
admin = newGroup(db, "Administrators");
|
||||||
admin.setDescription("Gerrit Site Administrators");
|
admin.setDescription("Gerrit Site Administrators");
|
||||||
GroupsUpdate.addNewGroup(db, admin);
|
GroupsUpdate.addNewGroup(db, admin);
|
||||||
index(admin);
|
index(InternalGroup.create(admin, ImmutableSet.of(), ImmutableSet.of()));
|
||||||
|
|
||||||
batch = newGroup(db, "Non-Interactive Users");
|
batch = newGroup(db, "Non-Interactive Users");
|
||||||
batch.setDescription("Users who perform batch actions on Gerrit");
|
batch.setDescription("Users who perform batch actions on Gerrit");
|
||||||
batch.setOwnerGroupUUID(admin.getGroupUUID());
|
batch.setOwnerGroupUUID(admin.getGroupUUID());
|
||||||
GroupsUpdate.addNewGroup(db, batch);
|
GroupsUpdate.addNewGroup(db, batch);
|
||||||
index(batch);
|
index(InternalGroup.create(batch, ImmutableSet.of(), ImmutableSet.of()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void index(AccountGroup group) throws IOException {
|
private void index(InternalGroup group) throws IOException {
|
||||||
for (GroupIndex groupIndex : indexCollection.getWriteIndexes()) {
|
for (GroupIndex groupIndex : indexCollection.getWriteIndexes()) {
|
||||||
groupIndex.replace(group);
|
groupIndex.replace(group);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,18 @@ package com.google.gerrit.server.query.group;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.common.base.CharMatcher;
|
import com.google.common.base.CharMatcher;
|
||||||
import com.google.gerrit.extensions.api.GerritApi;
|
import com.google.gerrit.extensions.api.GerritApi;
|
||||||
|
import com.google.gerrit.extensions.api.accounts.AccountInput;
|
||||||
import com.google.gerrit.extensions.api.groups.GroupInput;
|
import com.google.gerrit.extensions.api.groups.GroupInput;
|
||||||
import com.google.gerrit.extensions.api.groups.Groups.QueryRequest;
|
import com.google.gerrit.extensions.api.groups.Groups.QueryRequest;
|
||||||
import com.google.gerrit.extensions.common.AccountInfo;
|
import com.google.gerrit.extensions.common.AccountInfo;
|
||||||
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.index.FieldDef;
|
||||||
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.lifecycle.LifecycleManager;
|
import com.google.gerrit.lifecycle.LifecycleManager;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
@@ -39,7 +43,10 @@ import com.google.gerrit.server.account.AuthRequest;
|
|||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.config.AllProjectsName;
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
import com.google.gerrit.server.group.GroupsUpdate;
|
import com.google.gerrit.server.group.GroupsUpdate;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.group.ServerInitiated;
|
import com.google.gerrit.server.group.ServerInitiated;
|
||||||
|
import com.google.gerrit.server.index.group.GroupField;
|
||||||
|
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||||
import com.google.gerrit.server.schema.SchemaCreator;
|
import com.google.gerrit.server.schema.SchemaCreator;
|
||||||
import com.google.gerrit.server.util.ManualRequestContext;
|
import com.google.gerrit.server.util.ManualRequestContext;
|
||||||
import com.google.gerrit.server.util.OneOffRequestContext;
|
import com.google.gerrit.server.util.OneOffRequestContext;
|
||||||
@@ -99,6 +106,8 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
|
|
||||||
@Inject @ServerInitiated protected Provider<GroupsUpdate> groupsUpdateProvider;
|
@Inject @ServerInitiated protected Provider<GroupsUpdate> groupsUpdateProvider;
|
||||||
|
|
||||||
|
@Inject protected GroupIndexCollection indexes;
|
||||||
|
|
||||||
protected LifecycleManager lifecycle;
|
protected LifecycleManager lifecycle;
|
||||||
protected Injector injector;
|
protected Injector injector;
|
||||||
protected ReviewDb db;
|
protected ReviewDb db;
|
||||||
@@ -121,7 +130,8 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
db = schemaFactory.open();
|
db = schemaFactory.open();
|
||||||
schemaCreator.create(db);
|
schemaCreator.create(db);
|
||||||
|
|
||||||
Account.Id userId = createAccount("user", "User", "user@example.com", true);
|
Account.Id userId =
|
||||||
|
createAccountOutsideRequestContext("user", "User", "user@example.com", true);
|
||||||
user = userFactory.create(userId);
|
user = userFactory.create(userId);
|
||||||
requestContext.setContext(newRequestContext(userId));
|
requestContext.setContext(newRequestContext(userId));
|
||||||
currentUserInfo = gApi.accounts().id(userId.get()).get();
|
currentUserInfo = gApi.accounts().id(userId.get()).get();
|
||||||
@@ -162,7 +172,9 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
if (lifecycle != null) {
|
if (lifecycle != null) {
|
||||||
lifecycle.stop();
|
lifecycle.stop();
|
||||||
}
|
}
|
||||||
requestContext.setContext(null);
|
if (requestContext != null) {
|
||||||
|
requestContext.setContext(null);
|
||||||
|
}
|
||||||
if (db != null) {
|
if (db != null) {
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
@@ -246,6 +258,58 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
assertQuery("is:visibletoall", groupThatIsVisibleToAll);
|
assertQuery("is:visibletoall", groupThatIsVisibleToAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void byMember() throws Exception {
|
||||||
|
if (getSchemaVersion() < 4) {
|
||||||
|
assertMissingField(GroupField.MEMBER);
|
||||||
|
assertFailingQuery(
|
||||||
|
"member:someName", "'member' operator is not supported by group index version");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountInfo user1 = createAccount("user1", "User1", "user1@example.com");
|
||||||
|
AccountInfo user2 = createAccount("user2", "User2", "user2@example.com");
|
||||||
|
|
||||||
|
GroupInfo group1 = createGroup(name("group1"), user1);
|
||||||
|
GroupInfo group2 = createGroup(name("group2"), user2);
|
||||||
|
GroupInfo group3 = createGroup(name("group3"), user1);
|
||||||
|
|
||||||
|
assertQuery("member:" + user1.name, group1, group3);
|
||||||
|
assertQuery("member:" + user1.email, group1, group3);
|
||||||
|
|
||||||
|
gApi.groups().id(group3.id).removeMembers(user1.username);
|
||||||
|
gApi.groups().id(group2.id).addMembers(user1.username);
|
||||||
|
|
||||||
|
assertQuery("member:" + user1.name, group1, group2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bySubgroups() throws Exception {
|
||||||
|
if (getSchemaVersion() < 4) {
|
||||||
|
assertMissingField(GroupField.SUBGROUP);
|
||||||
|
assertFailingQuery(
|
||||||
|
"subgroup:someGroupName", "'subgroup' operator is not supported by group index version");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupInfo superParentGroup = createGroup(name("superParentGroup"));
|
||||||
|
GroupInfo parentGroup1 = createGroup(name("parentGroup1"));
|
||||||
|
GroupInfo parentGroup2 = createGroup(name("parentGroup2"));
|
||||||
|
GroupInfo subGroup = createGroup(name("subGroup"));
|
||||||
|
|
||||||
|
gApi.groups().id(superParentGroup.id).addGroups(parentGroup1.id, parentGroup2.id);
|
||||||
|
gApi.groups().id(parentGroup1.id).addGroups(subGroup.id);
|
||||||
|
gApi.groups().id(parentGroup2.id).addGroups(subGroup.id);
|
||||||
|
|
||||||
|
assertQuery("subgroup:" + subGroup.id, parentGroup1, parentGroup2);
|
||||||
|
assertQuery("subgroup:" + parentGroup1.id, superParentGroup);
|
||||||
|
|
||||||
|
gApi.groups().id(superParentGroup.id).addGroups(subGroup.id);
|
||||||
|
gApi.groups().id(parentGroup1.id).removeGroups(subGroup.id);
|
||||||
|
|
||||||
|
assertQuery("subgroup:" + subGroup.id, superParentGroup, parentGroup2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void byDefaultField() throws Exception {
|
public void byDefaultField() throws Exception {
|
||||||
GroupInfo group1 = createGroup(name("foo-group"));
|
GroupInfo group1 = createGroup(name("foo-group"));
|
||||||
@@ -313,8 +377,8 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
assertQuery("description:" + newDescription, group1);
|
assertQuery("description:" + newDescription, group1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account.Id createAccount(String username, String fullName, String email, boolean active)
|
private Account.Id createAccountOutsideRequestContext(
|
||||||
throws Exception {
|
String username, String fullName, String email, boolean active) throws Exception {
|
||||||
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
|
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
|
||||||
Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
|
Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
|
||||||
if (email != null) {
|
if (email != null) {
|
||||||
@@ -333,6 +397,16 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AccountInfo createAccount(String username, String fullName, String email)
|
||||||
|
throws Exception {
|
||||||
|
String uniqueName = name(username);
|
||||||
|
AccountInput accountInput = new AccountInput();
|
||||||
|
accountInput.username = uniqueName;
|
||||||
|
accountInput.name = fullName;
|
||||||
|
accountInput.email = email;
|
||||||
|
return gApi.accounts().create(accountInput).get();
|
||||||
|
}
|
||||||
|
|
||||||
protected GroupInfo createGroup(String name, AccountInfo... members) throws Exception {
|
protected GroupInfo createGroup(String name, AccountInfo... members) throws Exception {
|
||||||
return createGroupWithDescription(name, null, members);
|
return createGroupWithDescription(name, null, members);
|
||||||
}
|
}
|
||||||
@@ -452,4 +526,27 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
|
|
||||||
return name + "_" + getSanitizedMethodName();
|
return name + "_" + getSanitizedMethodName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void assertMissingField(FieldDef<InternalGroup, ?> field) {
|
||||||
|
assertThat(getSchema().hasField(field))
|
||||||
|
.named("schema %s has field %s", getSchemaVersion(), field.getName())
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertFailingQuery(String query, String expectedMessage) throws Exception {
|
||||||
|
try {
|
||||||
|
assertQuery(query);
|
||||||
|
fail("expected BadRequestException for query '" + query + "'");
|
||||||
|
} catch (BadRequestException e) {
|
||||||
|
assertThat(e.getMessage()).isEqualTo(expectedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getSchemaVersion() {
|
||||||
|
return getSchema().getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Schema<InternalGroup> getSchema() {
|
||||||
|
return indexes.getSearchIndex().getSchema();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import com.google.gerrit.extensions.restapi.RestApiException;
|
|||||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups;
|
|
||||||
import com.google.gerrit.server.group.AddMembers;
|
import com.google.gerrit.server.group.AddMembers;
|
||||||
|
import com.google.gerrit.server.group.AddSubgroups;
|
||||||
import com.google.gerrit.server.group.CreateGroup;
|
import com.google.gerrit.server.group.CreateGroup;
|
||||||
import com.google.gerrit.server.group.GroupResource;
|
import com.google.gerrit.server.group.GroupResource;
|
||||||
import com.google.gerrit.server.group.GroupsCollection;
|
import com.google.gerrit.server.group.GroupsCollection;
|
||||||
@@ -101,7 +101,7 @@ final class CreateGroupCommand extends SshCommand {
|
|||||||
|
|
||||||
@Inject private AddMembers addMembers;
|
@Inject private AddMembers addMembers;
|
||||||
|
|
||||||
@Inject private AddIncludedGroups addIncludedGroups;
|
@Inject private AddSubgroups addSubgroups;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Failure, OrmException, IOException, ConfigInvalidException {
|
protected void run() throws Failure, OrmException, IOException, ConfigInvalidException {
|
||||||
@@ -113,7 +113,7 @@ final class CreateGroupCommand extends SshCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!initialGroups.isEmpty()) {
|
if (!initialGroups.isEmpty()) {
|
||||||
addIncludedGroups(rsrc);
|
addSubgroups(rsrc);
|
||||||
}
|
}
|
||||||
} catch (RestApiException e) {
|
} catch (RestApiException e) {
|
||||||
throw die(e);
|
throw die(e);
|
||||||
@@ -142,10 +142,10 @@ final class CreateGroupCommand extends SshCommand {
|
|||||||
addMembers.apply(rsrc, input);
|
addMembers.apply(rsrc, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addIncludedGroups(GroupResource rsrc) throws RestApiException, OrmException {
|
private void addSubgroups(GroupResource rsrc) throws RestApiException, OrmException, IOException {
|
||||||
AddIncludedGroups.Input input =
|
AddSubgroups.Input input =
|
||||||
AddIncludedGroups.Input.fromGroups(
|
AddSubgroups.Input.fromGroups(
|
||||||
initialGroups.stream().map(AccountGroup.UUID::get).collect(toList()));
|
initialGroups.stream().map(AccountGroup.UUID::get).collect(toList()));
|
||||||
addIncludedGroups.apply(rsrc, input);
|
addSubgroups.apply(rsrc, input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ import com.google.gerrit.extensions.common.GroupInfo;
|
|||||||
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.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.group.ListGroups;
|
import com.google.gerrit.server.group.ListGroups;
|
||||||
import com.google.gerrit.server.ioutil.ColumnFormatter;
|
import com.google.gerrit.server.ioutil.ColumnFormatter;
|
||||||
import com.google.gerrit.sshd.CommandMetaData;
|
import com.google.gerrit.sshd.CommandMetaData;
|
||||||
import com.google.gerrit.sshd.SshCommand;
|
import com.google.gerrit.sshd.SshCommand;
|
||||||
import com.google.gerrit.util.cli.Options;
|
import com.google.gerrit.util.cli.Options;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
@CommandMetaData(
|
@CommandMetaData(
|
||||||
@@ -60,15 +62,15 @@ public class ListGroupsCommand extends SshCommand {
|
|||||||
for (GroupInfo info : listGroups.get()) {
|
for (GroupInfo info : listGroups.get()) {
|
||||||
formatter.addColumn(MoreObjects.firstNonNull(info.name, "n/a"));
|
formatter.addColumn(MoreObjects.firstNonNull(info.name, "n/a"));
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
AccountGroup o =
|
Optional<InternalGroup> group =
|
||||||
info.ownerId != null
|
info.ownerId != null
|
||||||
? groupCache.get(new AccountGroup.UUID(Url.decode(info.ownerId)))
|
? groupCache.get(new AccountGroup.UUID(Url.decode(info.ownerId)))
|
||||||
: null;
|
: Optional.empty();
|
||||||
|
|
||||||
formatter.addColumn(Url.decode(info.id));
|
formatter.addColumn(Url.decode(info.id));
|
||||||
formatter.addColumn(Strings.nullToEmpty(info.description));
|
formatter.addColumn(Strings.nullToEmpty(info.description));
|
||||||
formatter.addColumn(o != null ? o.getName() : "n/a");
|
formatter.addColumn(group.map(InternalGroup::getName).orElse("n/a"));
|
||||||
formatter.addColumn(o != null ? o.getGroupUUID().get() : "");
|
formatter.addColumn(group.map(g -> g.getGroupUUID().get()).orElse(""));
|
||||||
formatter.addColumn(
|
formatter.addColumn(
|
||||||
Boolean.toString(MoreObjects.firstNonNull(info.options.visibleToAll, Boolean.FALSE)));
|
Boolean.toString(MoreObjects.firstNonNull(info.options.visibleToAll, Boolean.FALSE)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import com.google.gerrit.extensions.common.AccountInfo;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.account.AccountLoader;
|
import com.google.gerrit.server.account.AccountLoader;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.account.GroupDetailFactory.Factory;
|
import com.google.gerrit.server.account.GroupControl;
|
||||||
import com.google.gerrit.server.group.ListMembers;
|
import com.google.gerrit.server.group.ListMembers;
|
||||||
import com.google.gerrit.server.ioutil.ColumnFormatter;
|
import com.google.gerrit.server.ioutil.ColumnFormatter;
|
||||||
import com.google.gerrit.sshd.CommandMetaData;
|
import com.google.gerrit.sshd.CommandMetaData;
|
||||||
@@ -61,9 +61,9 @@ public class ListMembersCommand extends SshCommand {
|
|||||||
@Inject
|
@Inject
|
||||||
protected ListMembersCommandImpl(
|
protected ListMembersCommandImpl(
|
||||||
GroupCache groupCache,
|
GroupCache groupCache,
|
||||||
Factory groupDetailFactory,
|
GroupControl.Factory groupControlFactory,
|
||||||
AccountLoader.Factory accountLoaderFactory) {
|
AccountLoader.Factory accountLoaderFactory) {
|
||||||
super(groupCache, groupDetailFactory, accountLoaderFactory);
|
super(groupCache, groupControlFactory, accountLoaderFactory);
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import static java.util.stream.Collectors.joining;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.collect.Streams;
|
||||||
import com.google.gerrit.extensions.restapi.IdString;
|
import com.google.gerrit.extensions.restapi.IdString;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
@@ -25,12 +26,13 @@ import com.google.gerrit.reviewdb.client.Account;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.group.AddIncludedGroups;
|
|
||||||
import com.google.gerrit.server.group.AddMembers;
|
import com.google.gerrit.server.group.AddMembers;
|
||||||
import com.google.gerrit.server.group.DeleteIncludedGroups;
|
import com.google.gerrit.server.group.AddSubgroups;
|
||||||
import com.google.gerrit.server.group.DeleteMembers;
|
import com.google.gerrit.server.group.DeleteMembers;
|
||||||
|
import com.google.gerrit.server.group.DeleteSubgroups;
|
||||||
import com.google.gerrit.server.group.GroupResource;
|
import com.google.gerrit.server.group.GroupResource;
|
||||||
import com.google.gerrit.server.group.GroupsCollection;
|
import com.google.gerrit.server.group.GroupsCollection;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.sshd.CommandMetaData;
|
import com.google.gerrit.sshd.CommandMetaData;
|
||||||
import com.google.gerrit.sshd.SshCommand;
|
import com.google.gerrit.sshd.SshCommand;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -92,9 +94,9 @@ public class SetMembersCommand extends SshCommand {
|
|||||||
|
|
||||||
@Inject private DeleteMembers deleteMembers;
|
@Inject private DeleteMembers deleteMembers;
|
||||||
|
|
||||||
@Inject private AddIncludedGroups addIncludedGroups;
|
@Inject private AddSubgroups addSubgroups;
|
||||||
|
|
||||||
@Inject private DeleteIncludedGroups deleteIncludedGroups;
|
@Inject private DeleteSubgroups deleteSubgroups;
|
||||||
|
|
||||||
@Inject private GroupsCollection groupsCollection;
|
@Inject private GroupsCollection groupsCollection;
|
||||||
|
|
||||||
@@ -113,7 +115,7 @@ public class SetMembersCommand extends SshCommand {
|
|||||||
reportMembersAction("removed from", resource, accountsToRemove);
|
reportMembersAction("removed from", resource, accountsToRemove);
|
||||||
}
|
}
|
||||||
if (!groupsToRemove.isEmpty()) {
|
if (!groupsToRemove.isEmpty()) {
|
||||||
deleteIncludedGroups.apply(resource, fromGroups(groupsToRemove));
|
deleteSubgroups.apply(resource, fromGroups(groupsToRemove));
|
||||||
reportGroupsAction("excluded from", resource, groupsToRemove);
|
reportGroupsAction("excluded from", resource, groupsToRemove);
|
||||||
}
|
}
|
||||||
if (!accountsToAdd.isEmpty()) {
|
if (!accountsToAdd.isEmpty()) {
|
||||||
@@ -121,7 +123,7 @@ public class SetMembersCommand extends SshCommand {
|
|||||||
reportMembersAction("added to", resource, accountsToAdd);
|
reportMembersAction("added to", resource, accountsToAdd);
|
||||||
}
|
}
|
||||||
if (!groupsToInclude.isEmpty()) {
|
if (!groupsToInclude.isEmpty()) {
|
||||||
addIncludedGroups.apply(resource, fromGroups(groupsToInclude));
|
addSubgroups.apply(resource, fromGroups(groupsToInclude));
|
||||||
reportGroupsAction("included to", resource, groupsToInclude);
|
reportGroupsAction("included to", resource, groupsToInclude);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,14 +151,17 @@ public class SetMembersCommand extends SshCommand {
|
|||||||
String action, GroupResource group, List<AccountGroup.UUID> groupUuidList)
|
String action, GroupResource group, List<AccountGroup.UUID> groupUuidList)
|
||||||
throws UnsupportedEncodingException, IOException {
|
throws UnsupportedEncodingException, IOException {
|
||||||
String names =
|
String names =
|
||||||
groupUuidList.stream().map(uuid -> groupCache.get(uuid).getName()).collect(joining(", "));
|
groupUuidList
|
||||||
|
.stream()
|
||||||
|
.map(uuid -> groupCache.get(uuid).map(InternalGroup::getName))
|
||||||
|
.flatMap(Streams::stream)
|
||||||
|
.collect(joining(", "));
|
||||||
out.write(
|
out.write(
|
||||||
String.format("Groups %s group %s: %s\n", action, group.getName(), names).getBytes(ENC));
|
String.format("Groups %s group %s: %s\n", action, group.getName(), names).getBytes(ENC));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AddIncludedGroups.Input fromGroups(List<AccountGroup.UUID> accounts) {
|
private AddSubgroups.Input fromGroups(List<AccountGroup.UUID> accounts) {
|
||||||
return AddIncludedGroups.Input.fromGroups(
|
return AddSubgroups.Input.fromGroups(accounts.stream().map(Object::toString).collect(toList()));
|
||||||
accounts.stream().map(Object::toString).collect(toList()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AddMembers.Input fromMembers(List<Account.Id> accounts) {
|
private AddMembers.Input fromMembers(List<Account.Id> accounts) {
|
||||||
|
|||||||
Reference in New Issue
Block a user