Merge changes from topic 'group-index'
* changes: Index groups that are created during init Implement group index for ElasticSearch Add REST endpoint to reindex a single group Support default query for groups Add query operator that matches groups that are visible to all users Add group query operator to match groups by owner Add group query operator to match groups by description Support querying groups by name and name part Set '_more_groups' on last group of query result Add tests for group index Groups API: Add query methods Support arbitrary group queries via REST Add new optional interface for REST collections to get request params Add GroupQueryProcessor IsVisibleToPredicate: Move describe method into base class Index group on creation and whenever a group is evicted from cache Initial implementation of group index in Lucene Add initial GroupQueryBuilder and define key predicate for group index Add schema and index definitions for groups index
This commit is contained in:
@@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Tracks group objects in memory for efficient access. */
|
||||
public interface GroupCache {
|
||||
AccountGroup get(AccountGroup.Id groupId);
|
||||
@@ -36,10 +38,10 @@ public interface GroupCache {
|
||||
ImmutableList<AccountGroup> all();
|
||||
|
||||
/** Notify the cache that a new group was constructed. */
|
||||
void onCreateGroup(AccountGroup.NameKey newGroupName);
|
||||
void onCreateGroup(AccountGroup.NameKey newGroupName) throws IOException;
|
||||
|
||||
void evict(AccountGroup group);
|
||||
void evict(AccountGroup group) throws IOException;
|
||||
|
||||
void evictAfterRename(final AccountGroup.NameKey oldName,
|
||||
final AccountGroup.NameKey newName);
|
||||
void evictAfterRename(AccountGroup.NameKey oldName,
|
||||
AccountGroup.NameKey newName) throws IOException;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,13 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupName;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.index.group.GroupIndexer;
|
||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
@@ -33,6 +35,7 @@ import com.google.inject.name.Named;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -76,17 +79,20 @@ public class GroupCacheImpl implements GroupCache {
|
||||
private final LoadingCache<String, Optional<AccountGroup>> byName;
|
||||
private final LoadingCache<String, Optional<AccountGroup>> byUUID;
|
||||
private final SchemaFactory<ReviewDb> schema;
|
||||
private final Provider<GroupIndexer> indexer;
|
||||
|
||||
@Inject
|
||||
GroupCacheImpl(
|
||||
@Named(BYID_NAME) LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId,
|
||||
@Named(BYNAME_NAME) LoadingCache<String, Optional<AccountGroup>> byName,
|
||||
@Named(BYUUID_NAME) LoadingCache<String, Optional<AccountGroup>> byUUID,
|
||||
SchemaFactory<ReviewDb> schema) {
|
||||
SchemaFactory<ReviewDb> schema,
|
||||
Provider<GroupIndexer> indexer) {
|
||||
this.byId = byId;
|
||||
this.byName = byName;
|
||||
this.byUUID = byUUID;
|
||||
this.schema = schema;
|
||||
this.indexer = indexer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +107,7 @@ public class GroupCacheImpl implements GroupCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evict(final AccountGroup group) {
|
||||
public void evict(final AccountGroup group) throws IOException {
|
||||
if (group.getId() != null) {
|
||||
byId.invalidate(group.getId());
|
||||
}
|
||||
@@ -111,17 +117,19 @@ public class GroupCacheImpl implements GroupCache {
|
||||
if (group.getGroupUUID() != null) {
|
||||
byUUID.invalidate(group.getGroupUUID().get());
|
||||
}
|
||||
indexer.get().index(group.getGroupUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictAfterRename(final AccountGroup.NameKey oldName,
|
||||
final AccountGroup.NameKey newName) {
|
||||
final AccountGroup.NameKey newName) throws IOException {
|
||||
if (oldName != null) {
|
||||
byName.invalidate(oldName.get());
|
||||
}
|
||||
if (newName != null) {
|
||||
byName.invalidate(newName.get());
|
||||
}
|
||||
indexer.get().index(get(newName).getGroupUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,8 +169,10 @@ public class GroupCacheImpl implements GroupCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateGroup(AccountGroup.NameKey newGroupName) {
|
||||
public void onCreateGroup(AccountGroup.NameKey newGroupName)
|
||||
throws IOException {
|
||||
byName.invalidate(newGroupName.get());
|
||||
indexer.get().index(get(newGroupName).getGroupUUID());
|
||||
}
|
||||
|
||||
private static AccountGroup missing(AccountGroup.Id key) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.google.gerrit.server.group.GetName;
|
||||
import com.google.gerrit.server.group.GetOptions;
|
||||
import com.google.gerrit.server.group.GetOwner;
|
||||
import com.google.gerrit.server.group.GroupResource;
|
||||
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.PutDescription;
|
||||
@@ -71,6 +72,7 @@ class GroupApiImpl implements GroupApi {
|
||||
private final DeleteIncludedGroups deleteGroups;
|
||||
private final GetAuditLog getAuditLog;
|
||||
private final GroupResource rsrc;
|
||||
private final Index index;
|
||||
|
||||
@AssistedInject
|
||||
GroupApiImpl(
|
||||
@@ -91,6 +93,7 @@ class GroupApiImpl implements GroupApi {
|
||||
AddIncludedGroups addGroups,
|
||||
DeleteIncludedGroups deleteGroups,
|
||||
GetAuditLog getAuditLog,
|
||||
Index index,
|
||||
@Assisted GroupResource rsrc) {
|
||||
this.getGroup = getGroup;
|
||||
this.getDetail = getDetail;
|
||||
@@ -109,6 +112,7 @@ class GroupApiImpl implements GroupApi {
|
||||
this.addGroups = addGroups;
|
||||
this.deleteGroups = deleteGroups;
|
||||
this.getAuditLog = getAuditLog;
|
||||
this.index = index;
|
||||
this.rsrc = rsrc;
|
||||
}
|
||||
|
||||
@@ -143,7 +147,7 @@ class GroupApiImpl implements GroupApi {
|
||||
putName.apply(rsrc, in);
|
||||
} catch (NoSuchGroupException e) {
|
||||
throw new ResourceNotFoundException(name, e);
|
||||
} catch (OrmException e) {
|
||||
} catch (OrmException | IOException e) {
|
||||
throw new RestApiException("Cannot put group name", e);
|
||||
}
|
||||
}
|
||||
@@ -163,7 +167,7 @@ class GroupApiImpl implements GroupApi {
|
||||
in.owner = owner;
|
||||
try {
|
||||
putOwner.apply(rsrc, in);
|
||||
} catch (OrmException e) {
|
||||
} catch (OrmException | IOException e) {
|
||||
throw new RestApiException("Cannot put group owner", e);
|
||||
}
|
||||
}
|
||||
@@ -179,7 +183,7 @@ class GroupApiImpl implements GroupApi {
|
||||
in.description = description;
|
||||
try {
|
||||
putDescription.apply(rsrc, in);
|
||||
} catch (OrmException e) {
|
||||
} catch (OrmException | IOException e) {
|
||||
throw new RestApiException("Cannot put group description", e);
|
||||
}
|
||||
}
|
||||
@@ -193,7 +197,7 @@ class GroupApiImpl implements GroupApi {
|
||||
public void options(GroupOptionsInfo options) throws RestApiException {
|
||||
try {
|
||||
putOptions.apply(rsrc, options);
|
||||
} catch (OrmException e) {
|
||||
} catch (OrmException | IOException e) {
|
||||
throw new RestApiException("Cannot put group options", e);
|
||||
}
|
||||
}
|
||||
@@ -270,4 +274,13 @@ class GroupApiImpl implements GroupApi {
|
||||
throw new RestApiException("Cannot get audit log", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void index() throws RestApiException {
|
||||
try {
|
||||
index.apply(rsrc, new Index.Input());
|
||||
} catch (IOException e) {
|
||||
throw new RestApiException("Cannot index group", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapa
|
||||
import com.google.gerrit.extensions.api.groups.GroupApi;
|
||||
import com.google.gerrit.extensions.api.groups.GroupInput;
|
||||
import com.google.gerrit.extensions.api.groups.Groups;
|
||||
import com.google.gerrit.extensions.client.ListGroupsOption;
|
||||
import com.google.gerrit.extensions.common.GroupInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
@@ -30,6 +31,7 @@ import com.google.gerrit.server.account.AccountsCollection;
|
||||
import com.google.gerrit.server.group.CreateGroup;
|
||||
import com.google.gerrit.server.group.GroupsCollection;
|
||||
import com.google.gerrit.server.group.ListGroups;
|
||||
import com.google.gerrit.server.group.QueryGroups;
|
||||
import com.google.gerrit.server.project.ProjectsCollection;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -37,6 +39,7 @@ import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
|
||||
@Singleton
|
||||
@@ -45,6 +48,7 @@ class GroupsImpl implements Groups {
|
||||
private final GroupsCollection groups;
|
||||
private final ProjectsCollection projects;
|
||||
private final Provider<ListGroups> listGroups;
|
||||
private final Provider<QueryGroups> queryGroups;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final CreateGroup.Factory createGroup;
|
||||
private final GroupApiImpl.Factory api;
|
||||
@@ -55,6 +59,7 @@ class GroupsImpl implements Groups {
|
||||
GroupsCollection groups,
|
||||
ProjectsCollection projects,
|
||||
Provider<ListGroups> listGroups,
|
||||
Provider<QueryGroups> queryGroups,
|
||||
Provider<CurrentUser> user,
|
||||
CreateGroup.Factory createGroup,
|
||||
GroupApiImpl.Factory api) {
|
||||
@@ -62,6 +67,7 @@ class GroupsImpl implements Groups {
|
||||
this.groups = groups;
|
||||
this.projects = projects;
|
||||
this.listGroups = listGroups;
|
||||
this.queryGroups = queryGroups;
|
||||
this.user = user;
|
||||
this.createGroup = createGroup;
|
||||
this.api = api;
|
||||
@@ -145,4 +151,35 @@ class GroupsImpl implements Groups {
|
||||
throw new RestApiException("Cannot list groups", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryRequest query() {
|
||||
return new QueryRequest() {
|
||||
@Override
|
||||
public List<GroupInfo> get() throws RestApiException {
|
||||
return GroupsImpl.this.query(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryRequest query(String query) {
|
||||
return query().withQuery(query);
|
||||
}
|
||||
|
||||
private List<GroupInfo> query(QueryRequest r)
|
||||
throws RestApiException {
|
||||
try {
|
||||
QueryGroups myQueryGroups = queryGroups.get();
|
||||
myQueryGroups.setQuery(r.getQuery());
|
||||
myQueryGroups.setLimit(r.getLimit());
|
||||
myQueryGroups.setStart(r.getStart());
|
||||
for (ListGroupsOption option : r.getOptions()) {
|
||||
myQueryGroups.addOption(option);
|
||||
}
|
||||
return myQueryGroups.apply(TopLevelResource.INSTANCE);
|
||||
} catch (OrmException e) {
|
||||
throw new RestApiException("Cannot query groups", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server.group;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gerrit.common.data.GroupDescription;
|
||||
import com.google.gerrit.common.data.GroupDescriptions;
|
||||
import com.google.gerrit.common.data.GroupReference;
|
||||
@@ -22,6 +23,7 @@ import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.AcceptsCreate;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.NeedsParams;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestCollection;
|
||||
import com.google.gerrit.extensions.restapi.RestView;
|
||||
@@ -35,34 +37,43 @@ import com.google.gerrit.server.account.GroupBackends;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class GroupsCollection implements
|
||||
RestCollection<TopLevelResource, GroupResource>,
|
||||
AcceptsCreate<TopLevelResource> {
|
||||
AcceptsCreate<TopLevelResource>, NeedsParams {
|
||||
private final DynamicMap<RestView<GroupResource>> views;
|
||||
private final Provider<ListGroups> list;
|
||||
private final Provider<QueryGroups> queryGroups;
|
||||
private final CreateGroup.Factory createGroup;
|
||||
private final GroupControl.Factory groupControlFactory;
|
||||
private final GroupBackend groupBackend;
|
||||
private final Provider<CurrentUser> self;
|
||||
|
||||
private boolean hasQuery2;
|
||||
|
||||
@Inject
|
||||
GroupsCollection(final DynamicMap<RestView<GroupResource>> views,
|
||||
final Provider<ListGroups> list,
|
||||
final CreateGroup.Factory createGroup,
|
||||
final GroupControl.Factory groupControlFactory,
|
||||
final GroupBackend groupBackend,
|
||||
final Provider<CurrentUser> self) {
|
||||
GroupsCollection(DynamicMap<RestView<GroupResource>> views,
|
||||
Provider<ListGroups> list,
|
||||
Provider<QueryGroups> queryGroups,
|
||||
CreateGroup.Factory createGroup,
|
||||
GroupControl.Factory groupControlFactory,
|
||||
GroupBackend groupBackend,
|
||||
Provider<CurrentUser> self) {
|
||||
this.views = views;
|
||||
this.list = list;
|
||||
this.queryGroups = queryGroups;
|
||||
this.createGroup = createGroup;
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
this.groupBackend = groupBackend;
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParams(Multimap<String, String> params) {
|
||||
// The --query2 option is defined in QueryGroups
|
||||
this.hasQuery2 = params.containsKey("query2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestView<TopLevelResource> list() throws ResourceNotFoundException,
|
||||
AuthException {
|
||||
@@ -73,6 +84,10 @@ public class GroupsCollection implements
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
if (hasQuery2) {
|
||||
return queryGroups.get();
|
||||
}
|
||||
|
||||
return list.get();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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.gerrit.common.data.GroupDescriptions;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.group.Index.Input;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Singleton
|
||||
public class Index implements RestModifyView<GroupResource, Input> {
|
||||
public static class Input {
|
||||
}
|
||||
|
||||
private final GroupCache groupCache;
|
||||
|
||||
@Inject
|
||||
Index(GroupCache groupCache) {
|
||||
this.groupCache = groupCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<?> apply(GroupResource rsrc, Input input)
|
||||
throws IOException, AuthException, UnprocessableEntityException {
|
||||
if (!rsrc.getControl().isOwner()) {
|
||||
throw new AuthException("not allowed to index group");
|
||||
}
|
||||
|
||||
AccountGroup group = GroupDescriptions.toAccountGroup(rsrc.getGroup());
|
||||
if (group == null) {
|
||||
throw new UnprocessableEntityException(String
|
||||
.format("External Group Not Allowed: %s", rsrc.getGroupUUID().get()));
|
||||
}
|
||||
|
||||
// evicting the group from the cache, reindexes the account
|
||||
groupCache.evict(group);
|
||||
return Response.none();
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
||||
this.owned = owned;
|
||||
}
|
||||
|
||||
@Option(name = "-q", usage = "group to inspect")
|
||||
@Option(name = "--query", aliases = {"-q"}, usage = "group to inspect")
|
||||
public void addGroup(AccountGroup.UUID id) {
|
||||
groupsToInspect.add(id);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public class Module extends RestApiModule {
|
||||
get(GROUP_KIND).to(GetGroup.class);
|
||||
put(GROUP_KIND).to(PutGroup.class);
|
||||
get(GROUP_KIND, "detail").to(GetDetail.class);
|
||||
post(GROUP_KIND, "index").to(Index.class);
|
||||
post(GROUP_KIND, "members").to(AddMembers.class);
|
||||
post(GROUP_KIND, "members.add").to(AddMembers.class);
|
||||
post(GROUP_KIND, "members.delete").to(DeleteMembers.class);
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
@Singleton
|
||||
@@ -51,7 +52,7 @@ public class PutDescription implements RestModifyView<GroupResource, Input> {
|
||||
@Override
|
||||
public Response<String> apply(GroupResource resource, Input input)
|
||||
throws AuthException, MethodNotAllowedException,
|
||||
ResourceNotFoundException, OrmException {
|
||||
ResourceNotFoundException, OrmException, IOException {
|
||||
if (input == null) {
|
||||
input = new Input(); // Delete would set description to null.
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
@@ -70,7 +71,8 @@ public class PutName implements RestModifyView<GroupResource, Input> {
|
||||
@Override
|
||||
public String apply(GroupResource rsrc, Input input)
|
||||
throws MethodNotAllowedException, AuthException, BadRequestException,
|
||||
ResourceConflictException, OrmException, NoSuchGroupException {
|
||||
ResourceConflictException, OrmException, NoSuchGroupException,
|
||||
IOException {
|
||||
if (rsrc.toAccountGroup() == null) {
|
||||
throw new MethodNotAllowedException();
|
||||
} else if (!rsrc.getControl().isOwner()) {
|
||||
@@ -92,7 +94,7 @@ public class PutName implements RestModifyView<GroupResource, Input> {
|
||||
|
||||
private GroupDetail renameGroup(AccountGroup group, String newName)
|
||||
throws ResourceConflictException, OrmException,
|
||||
NoSuchGroupException {
|
||||
NoSuchGroupException, IOException {
|
||||
AccountGroup.Id groupId = group.getId();
|
||||
AccountGroup.NameKey old = group.getNameKey();
|
||||
AccountGroup.NameKey key = new AccountGroup.NameKey(newName);
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
@Singleton
|
||||
@@ -45,7 +46,7 @@ public class PutOptions
|
||||
@Override
|
||||
public GroupOptionsInfo apply(GroupResource resource, GroupOptionsInfo input)
|
||||
throws MethodNotAllowedException, AuthException, BadRequestException,
|
||||
ResourceNotFoundException, OrmException {
|
||||
ResourceNotFoundException, OrmException, IOException {
|
||||
if (resource.toAccountGroup() == null) {
|
||||
throw new MethodNotAllowedException();
|
||||
} else if (!resource.getControl().isOwner()) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
@Singleton
|
||||
@@ -60,7 +61,7 @@ public class PutOwner implements RestModifyView<GroupResource, Input> {
|
||||
public GroupInfo apply(GroupResource resource, Input input)
|
||||
throws ResourceNotFoundException, MethodNotAllowedException,
|
||||
AuthException, BadRequestException, UnprocessableEntityException,
|
||||
OrmException {
|
||||
OrmException, IOException {
|
||||
AccountGroup group = resource.toAccountGroup();
|
||||
if (group == null) {
|
||||
throw new MethodNotAllowedException();
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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.common.base.Strings;
|
||||
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.common.GroupInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.group.GroupIndex;
|
||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gerrit.server.query.QueryResult;
|
||||
import com.google.gerrit.server.query.group.GroupQueryBuilder;
|
||||
import com.google.gerrit.server.query.group.GroupQueryProcessor;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class QueryGroups implements RestReadView<TopLevelResource> {
|
||||
private final GroupIndexCollection indexes;
|
||||
private final GroupQueryBuilder queryBuilder;
|
||||
private final GroupQueryProcessor queryProcessor;
|
||||
private final GroupJson json;
|
||||
|
||||
private String query;
|
||||
private int limit;
|
||||
private int start;
|
||||
private EnumSet<ListGroupsOption> options =
|
||||
EnumSet.noneOf(ListGroupsOption.class);
|
||||
|
||||
/** --query (-q) is already used by {@link ListGroups} */
|
||||
@Option(name = "--query2", aliases = {"-q2"}, usage = "group query")
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT",
|
||||
usage = "maximum number of groups to list")
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Option(name = "--start", aliases = {"-S"}, metaVar = "CNT",
|
||||
usage = "number of groups to skip")
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
@Option(name = "-o", usage = "Output options per group")
|
||||
public void addOption(ListGroupsOption o) {
|
||||
options.add(o);
|
||||
}
|
||||
|
||||
@Option(name = "-O", usage = "Output option flags, in hex")
|
||||
public void setOptionFlagsHex(String hex) {
|
||||
options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected QueryGroups(GroupIndexCollection indexes,
|
||||
GroupQueryBuilder queryBuilder,
|
||||
GroupQueryProcessor queryProcessor,
|
||||
GroupJson json) {
|
||||
this.indexes = indexes;
|
||||
this.queryBuilder = queryBuilder;
|
||||
this.queryProcessor = queryProcessor;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupInfo> apply(TopLevelResource resource)
|
||||
throws BadRequestException, MethodNotAllowedException, OrmException {
|
||||
if (Strings.isNullOrEmpty(query)) {
|
||||
throw new BadRequestException("missing query field");
|
||||
}
|
||||
|
||||
GroupIndex searchIndex = indexes.getSearchIndex();
|
||||
if (searchIndex == null) {
|
||||
throw new MethodNotAllowedException("no group index");
|
||||
}
|
||||
|
||||
if (start != 0) {
|
||||
queryProcessor.setStart(start);
|
||||
}
|
||||
|
||||
if (limit != 0) {
|
||||
queryProcessor.setLimit(limit);
|
||||
}
|
||||
|
||||
try {
|
||||
QueryResult<AccountGroup> result =
|
||||
queryProcessor.query(queryBuilder.parse(query));
|
||||
List<AccountGroup> groups = result.entities();
|
||||
|
||||
ArrayList<GroupInfo> groupInfos =
|
||||
Lists.newArrayListWithCapacity(groups.size());
|
||||
json.addOptions(options);
|
||||
for (AccountGroup group : groups) {
|
||||
groupInfos.add(json.format(GroupDescriptions.forAccountGroup(group)));
|
||||
}
|
||||
if (!groupInfos.isEmpty() && result.more()) {
|
||||
groupInfos.get(groupInfos.size() - 1)._moreGroups = true;
|
||||
}
|
||||
return groupInfos;
|
||||
} catch (QueryParseException e) {
|
||||
throw new BadRequestException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,12 @@
|
||||
|
||||
package com.google.gerrit.server.index;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.index.account.AccountIndex;
|
||||
import com.google.gerrit.server.index.change.ChangeIndex;
|
||||
import com.google.gerrit.server.index.change.DummyChangeIndex;
|
||||
import com.google.gerrit.server.index.group.GroupIndex;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
@@ -36,6 +38,13 @@ public class DummyIndexModule extends AbstractModule {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyGroupIndexFactory implements GroupIndex.Factory {
|
||||
@Override
|
||||
public GroupIndex create(Schema<AccountGroup> schema) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new IndexModule(1));
|
||||
@@ -43,5 +52,6 @@ public class DummyIndexModule extends AbstractModule {
|
||||
bind(Index.class).toInstance(new DummyChangeIndex());
|
||||
bind(AccountIndex.Factory.class).toInstance(new DummyAccountIndexFactory());
|
||||
bind(ChangeIndex.Factory.class).toInstance(new DummyChangeIndexFactory());
|
||||
bind(GroupIndex.Factory.class).toInstance(new DummyGroupIndexFactory());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.index;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
* Definition of an index over a Gerrit data type.
|
||||
@@ -32,13 +33,13 @@ public abstract class IndexDefinition<K, V, I extends Index<K, V>> {
|
||||
private final SchemaDefinitions<V> schemaDefs;
|
||||
private final IndexCollection<K, V, I> indexCollection;
|
||||
private final IndexFactory<K, V, I> indexFactory;
|
||||
private final SiteIndexer<K, V, I> siteIndexer;
|
||||
private final Provider<SiteIndexer<K, V, I>> siteIndexer;
|
||||
|
||||
protected IndexDefinition(
|
||||
SchemaDefinitions<V> schemaDefs,
|
||||
IndexCollection<K, V, I> indexCollection,
|
||||
IndexFactory<K, V, I> indexFactory,
|
||||
SiteIndexer<K, V, I> siteIndexer) {
|
||||
Provider<SiteIndexer<K, V, I>> siteIndexer) {
|
||||
this.schemaDefs = schemaDefs;
|
||||
this.indexCollection = indexCollection;
|
||||
this.indexFactory = indexFactory;
|
||||
@@ -66,6 +67,6 @@ public abstract class IndexDefinition<K, V, I extends Index<K, V>> {
|
||||
}
|
||||
|
||||
public final SiteIndexer<K, V, I> getSiteIndexer() {
|
||||
return siteIndexer;
|
||||
return siteIndexer.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,12 @@ import com.google.gerrit.server.index.change.ChangeIndexDefinition;
|
||||
import com.google.gerrit.server.index.change.ChangeIndexRewriter;
|
||||
import com.google.gerrit.server.index.change.ChangeIndexer;
|
||||
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
|
||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||
import com.google.gerrit.server.index.group.GroupIndexDefinition;
|
||||
import com.google.gerrit.server.index.group.GroupIndexRewriter;
|
||||
import com.google.gerrit.server.index.group.GroupIndexer;
|
||||
import com.google.gerrit.server.index.group.GroupIndexerImpl;
|
||||
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provides;
|
||||
@@ -61,7 +67,8 @@ public class IndexModule extends LifecycleModule {
|
||||
public static final ImmutableCollection<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
|
||||
ImmutableList.<SchemaDefinitions<?>> of(
|
||||
AccountSchemaDefinitions.INSTANCE,
|
||||
ChangeSchemaDefinitions.INSTANCE);
|
||||
ChangeSchemaDefinitions.INSTANCE,
|
||||
GroupSchemaDefinitions.INSTANCE);
|
||||
|
||||
/** Type of secondary index. */
|
||||
public static IndexType getIndexType(Injector injector) {
|
||||
@@ -98,15 +105,22 @@ public class IndexModule extends LifecycleModule {
|
||||
bind(ChangeIndexCollection.class);
|
||||
listener().to(ChangeIndexCollection.class);
|
||||
factory(ChangeIndexer.Factory.class);
|
||||
|
||||
bind(GroupIndexRewriter.class);
|
||||
bind(GroupIndexCollection.class);
|
||||
listener().to(GroupIndexCollection.class);
|
||||
factory(GroupIndexerImpl.Factory.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
Collection<IndexDefinition<?, ?, ?>> getIndexDefinitions(
|
||||
AccountIndexDefinition accounts,
|
||||
ChangeIndexDefinition changes) {
|
||||
ChangeIndexDefinition changes,
|
||||
GroupIndexDefinition groups) {
|
||||
Collection<IndexDefinition<?, ?, ?>> result =
|
||||
ImmutableList.<IndexDefinition<?, ?, ?>> of(
|
||||
accounts,
|
||||
groups,
|
||||
changes);
|
||||
Set<String> expected = FluentIterable.from(ALL_SCHEMA_DEFS)
|
||||
.transform(SchemaDefinitions::getName)
|
||||
@@ -140,6 +154,13 @@ public class IndexModule extends LifecycleModule {
|
||||
return factory.create(executor, indexes);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
GroupIndexer getGroupIndexer(GroupIndexerImpl.Factory factory,
|
||||
GroupIndexCollection indexes) {
|
||||
return factory.create(indexes);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@IndexExecutor(INTERACTIVE)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.index;
|
||||
|
||||
import static com.google.gerrit.server.index.account.AccountField.ID;
|
||||
import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
|
||||
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
|
||||
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
|
||||
@@ -23,6 +22,8 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.index.account.AccountField;
|
||||
import com.google.gerrit.server.index.group.GroupField;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@@ -47,9 +48,9 @@ public final class IndexUtils {
|
||||
|
||||
public static Set<String> accountFields(QueryOptions opts) {
|
||||
Set<String> fs = opts.fields();
|
||||
return fs.contains(ID.getName())
|
||||
return fs.contains(AccountField.ID.getName())
|
||||
? fs
|
||||
: Sets.union(fs, ImmutableSet.of(ID.getName()));
|
||||
: Sets.union(fs, ImmutableSet.of(AccountField.ID.getName()));
|
||||
}
|
||||
|
||||
public static Set<String> changeFields(QueryOptions opts) {
|
||||
@@ -68,6 +69,13 @@ public final class IndexUtils {
|
||||
ImmutableSet.of(LEGACY_ID.getName(), PROJECT.getName()));
|
||||
}
|
||||
|
||||
public static Set<String> groupFields(QueryOptions opts) {
|
||||
Set<String> fs = opts.fields();
|
||||
return fs.contains(GroupField.UUID.getName())
|
||||
? fs
|
||||
: Sets.union(fs, ImmutableSet.of(GroupField.UUID.getName()));
|
||||
}
|
||||
|
||||
private IndexUtils() {
|
||||
// hide default constructor
|
||||
}
|
||||
|
||||
@@ -90,12 +90,16 @@ public class SchemaUtil {
|
||||
if (person == null) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
return getPersonParts(
|
||||
return getNameParts(
|
||||
person.getName(),
|
||||
Collections.singleton(person.getEmailAddress()));
|
||||
}
|
||||
|
||||
public static Set<String> getPersonParts(String name,
|
||||
public static Set<String> getNameParts(String name) {
|
||||
return getNameParts(name, Collections.emptySet());
|
||||
}
|
||||
|
||||
public static Set<String> getNameParts(String name,
|
||||
Iterable<String> emails) {
|
||||
Splitter at = Splitter.on('@');
|
||||
Splitter s = Splitter.on(CharMatcher.anyOf("@.- ")).omitEmptyStrings();
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.util.Set;
|
||||
|
||||
@Singleton
|
||||
public class SingleVersionModule extends LifecycleModule {
|
||||
static final String SINGLE_VERSIONS = "IndexModule/SingleVersions";
|
||||
public static final String SINGLE_VERSIONS = "IndexModule/SingleVersions";
|
||||
|
||||
private final Map<String, Integer> singleVersions;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SingleVersionModule extends LifecycleModule {
|
||||
}
|
||||
|
||||
@Singleton
|
||||
static class SingleVersionListener implements LifecycleListener {
|
||||
public static class SingleVersionListener implements LifecycleListener {
|
||||
private final Set<String> disabled;
|
||||
private final Collection<IndexDefinition<?, ?, ?>> defs;
|
||||
private final Map<String, Integer> singleVersions;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class AccountField {
|
||||
@Override
|
||||
public Iterable<String> get(AccountState input, FillArgs args) {
|
||||
String fullName = input.getAccount().getFullName();
|
||||
Set<String> parts = SchemaUtil.getPersonParts(
|
||||
Set<String> parts = SchemaUtil.getNameParts(
|
||||
fullName,
|
||||
Iterables.transform(
|
||||
input.getExternalIds(),
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.index.IndexDefinition;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
public class AccountIndexDefinition
|
||||
extends IndexDefinition<Account.Id, AccountState, AccountIndex> {
|
||||
@@ -28,6 +29,6 @@ public class AccountIndexDefinition
|
||||
AccountIndex.Factory indexFactory,
|
||||
AllAccountsIndexer allAccountsIndexer) {
|
||||
super(AccountSchemaDefinitions.INSTANCE, indexCollection, indexFactory,
|
||||
allAccountsIndexer);
|
||||
Providers.of(allAccountsIndexer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.index.IndexDefinition;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
public class ChangeIndexDefinition
|
||||
extends IndexDefinition<Change.Id, ChangeData, ChangeIndex> {
|
||||
@@ -28,6 +29,6 @@ public class ChangeIndexDefinition
|
||||
ChangeIndex.Factory indexFactory,
|
||||
AllChangesIndexer allChangesIndexer) {
|
||||
super(ChangeSchemaDefinitions.INSTANCE, indexCollection, indexFactory,
|
||||
allChangesIndexer);
|
||||
Providers.of(allChangesIndexer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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.index.group;
|
||||
|
||||
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.index.IndexExecutor;
|
||||
import com.google.gerrit.server.index.SiteIndexer;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.TextProgressMonitor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Singleton
|
||||
public class AllGroupsIndexer
|
||||
extends SiteIndexer<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(AllGroupsIndexer.class);
|
||||
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final ListeningExecutorService executor;
|
||||
private final GroupCache groupCache;
|
||||
|
||||
@Inject
|
||||
AllGroupsIndexer(
|
||||
SchemaFactory<ReviewDb> schemaFactory,
|
||||
@IndexExecutor(BATCH) ListeningExecutorService executor,
|
||||
GroupCache groupCache) {
|
||||
this.schemaFactory = schemaFactory;
|
||||
this.executor = executor;
|
||||
this.groupCache = groupCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SiteIndexer.Result indexAll(GroupIndex index) {
|
||||
ProgressMonitor progress =
|
||||
new TextProgressMonitor(new PrintWriter(progressOut));
|
||||
progress.start(2);
|
||||
Stopwatch sw = Stopwatch.createStarted();
|
||||
List<AccountGroup.UUID> uuids;
|
||||
try {
|
||||
uuids = collectGroups(progress);
|
||||
} catch (OrmException e) {
|
||||
log.error("Error collecting groups", e);
|
||||
return new SiteIndexer.Result(sw, false, 0, 0);
|
||||
}
|
||||
return reindexGroups(index, uuids, progress);
|
||||
}
|
||||
|
||||
private SiteIndexer.Result reindexGroups(GroupIndex index,
|
||||
List<AccountGroup.UUID> uuids, ProgressMonitor progress) {
|
||||
progress.beginTask("Reindexing groups", uuids.size());
|
||||
List<ListenableFuture<?>> futures = new ArrayList<>(uuids.size());
|
||||
AtomicBoolean ok = new AtomicBoolean(true);
|
||||
final AtomicInteger done = new AtomicInteger();
|
||||
final AtomicInteger failed = new AtomicInteger();
|
||||
Stopwatch sw = Stopwatch.createStarted();
|
||||
for (final AccountGroup.UUID uuid : uuids) {
|
||||
final String desc = "group " + uuid;
|
||||
ListenableFuture<?> future = executor.submit(
|
||||
new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
try {
|
||||
AccountGroup oldGroup = groupCache.get(uuid);
|
||||
if (oldGroup != null) {
|
||||
groupCache.evict(oldGroup);
|
||||
}
|
||||
index.replace(groupCache.get(uuid));
|
||||
verboseWriter.println("Reindexed " + desc);
|
||||
done.incrementAndGet();
|
||||
} catch (Exception e) {
|
||||
failed.incrementAndGet();
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
addErrorListener(future, desc, progress, ok);
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
try {
|
||||
Futures.successfulAsList(futures).get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
log.error("Error waiting on group futures", e);
|
||||
return new SiteIndexer.Result(sw, false, 0, 0);
|
||||
}
|
||||
|
||||
progress.endTask();
|
||||
return new SiteIndexer.Result(sw, ok.get(), done.get(), failed.get());
|
||||
}
|
||||
|
||||
private List<AccountGroup.UUID> collectGroups(ProgressMonitor progress)
|
||||
throws OrmException {
|
||||
progress.beginTask("Collecting groups", ProgressMonitor.UNKNOWN);
|
||||
List<AccountGroup.UUID> uuids = new ArrayList<>();
|
||||
try (ReviewDb db = schemaFactory.open()) {
|
||||
for (AccountGroup group : db.accountGroups().all()) {
|
||||
uuids.add(group.getGroupUUID());
|
||||
}
|
||||
}
|
||||
progress.endTask();
|
||||
return uuids;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.FieldDef;
|
||||
import com.google.gerrit.server.index.FieldType;
|
||||
import com.google.gerrit.server.index.SchemaUtil;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
|
||||
/** Secondary index schemas for groups. */
|
||||
public class GroupField {
|
||||
/** Legacy group ID. */
|
||||
public static final FieldDef<AccountGroup, Integer> ID =
|
||||
new FieldDef.Single<AccountGroup, Integer>(
|
||||
"id", FieldType.INTEGER, false) {
|
||||
@Override
|
||||
public Integer get(AccountGroup input, FillArgs args) {
|
||||
return input.getId().get();
|
||||
}
|
||||
};
|
||||
|
||||
/** Group UUID. */
|
||||
public static final FieldDef<AccountGroup, String> UUID =
|
||||
new FieldDef.Single<AccountGroup, String>(
|
||||
"uuid", FieldType.EXACT, true) {
|
||||
@Override
|
||||
public String get(AccountGroup input, FillArgs args) {
|
||||
return input.getGroupUUID().get();
|
||||
}
|
||||
};
|
||||
|
||||
/** Group owner UUID. */
|
||||
public static final FieldDef<AccountGroup, String> OWNER_UUID =
|
||||
new FieldDef.Single<AccountGroup, String>(
|
||||
"owner_uuid", FieldType.EXACT, false) {
|
||||
@Override
|
||||
public String get(AccountGroup input, FillArgs args) {
|
||||
return input.getOwnerGroupUUID().get();
|
||||
}
|
||||
};
|
||||
|
||||
/** Group name. */
|
||||
public static final FieldDef<AccountGroup, String> NAME =
|
||||
new FieldDef.Single<AccountGroup, String>(
|
||||
"name", FieldType.EXACT, false) {
|
||||
@Override
|
||||
public String get(AccountGroup input, FillArgs args) {
|
||||
return input.getName();
|
||||
}
|
||||
};
|
||||
|
||||
/** Fuzzy prefix match on group name parts. */
|
||||
public static final FieldDef<AccountGroup, Iterable<String>> NAME_PART =
|
||||
new FieldDef.Repeatable<AccountGroup, String>(
|
||||
"name_part", FieldType.PREFIX, false) {
|
||||
@Override
|
||||
public Iterable<String> get(AccountGroup input, FillArgs args) {
|
||||
return SchemaUtil.getNameParts(input.getName());
|
||||
}
|
||||
};
|
||||
|
||||
/** Group description. */
|
||||
public static final FieldDef<AccountGroup, String> DESCRIPTION =
|
||||
new FieldDef.Single<AccountGroup, String>(
|
||||
"description", FieldType.FULL_TEXT, false) {
|
||||
@Override
|
||||
public String get(AccountGroup input, FillArgs args) {
|
||||
return input.getDescription();
|
||||
}
|
||||
};
|
||||
|
||||
/** Whether the group is visible to all users. */
|
||||
public static final FieldDef<AccountGroup, String> IS_VISIBLE_TO_ALL =
|
||||
new FieldDef.Single<AccountGroup, String>(
|
||||
"is_visible_to_all", FieldType.EXACT, false) {
|
||||
@Override
|
||||
public String get(AccountGroup input, FillArgs args)
|
||||
throws OrmException {
|
||||
return input.isVisibleToAll() ? "1" : "0";
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.Index;
|
||||
import com.google.gerrit.server.index.IndexDefinition;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.group.GroupPredicates;
|
||||
|
||||
public interface GroupIndex extends Index<AccountGroup.UUID, AccountGroup> {
|
||||
public interface Factory extends
|
||||
IndexDefinition.IndexFactory<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
||||
}
|
||||
|
||||
@Override
|
||||
default Predicate<AccountGroup> keyPredicate(AccountGroup.UUID uuid) {
|
||||
return GroupPredicates.uuid(uuid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.IndexCollection;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class GroupIndexCollection
|
||||
extends IndexCollection<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
||||
@Inject
|
||||
@VisibleForTesting
|
||||
public GroupIndexCollection() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.IndexDefinition;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
public class GroupIndexDefinition
|
||||
extends IndexDefinition<AccountGroup.UUID, AccountGroup, GroupIndex> {
|
||||
|
||||
@Inject
|
||||
GroupIndexDefinition(GroupIndexCollection indexCollection,
|
||||
GroupIndex.Factory indexFactory,
|
||||
@Nullable AllGroupsIndexer allGroupsIndexer) {
|
||||
super(GroupSchemaDefinitions.INSTANCE, indexCollection, indexFactory,
|
||||
Providers.of(allGroupsIndexer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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.index.group;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.IndexRewriter;
|
||||
import com.google.gerrit.server.index.QueryOptions;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class GroupIndexRewriter implements IndexRewriter<AccountGroup> {
|
||||
private final GroupIndexCollection indexes;
|
||||
|
||||
@Inject
|
||||
GroupIndexRewriter(GroupIndexCollection indexes) {
|
||||
this.indexes = indexes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<AccountGroup> rewrite(Predicate<AccountGroup> in,
|
||||
QueryOptions opts) throws QueryParseException {
|
||||
GroupIndex index = indexes.getSearchIndex();
|
||||
checkNotNull(index, "no active search index configured for groups");
|
||||
return new IndexedGroupQuery(index, in, opts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface GroupIndexer {
|
||||
|
||||
/**
|
||||
* Synchronously index a group.
|
||||
*
|
||||
* @param uuid group UUID to index.
|
||||
*/
|
||||
void index(AccountGroup.UUID uuid) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.index.Index;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.AssistedInject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class GroupIndexerImpl implements GroupIndexer {
|
||||
public interface Factory {
|
||||
GroupIndexerImpl create(GroupIndexCollection indexes);
|
||||
GroupIndexerImpl create(@Nullable GroupIndex index);
|
||||
}
|
||||
|
||||
private final GroupCache groupCache;
|
||||
private final GroupIndexCollection indexes;
|
||||
private final GroupIndex index;
|
||||
|
||||
@AssistedInject
|
||||
GroupIndexerImpl(GroupCache groupCache,
|
||||
@Assisted GroupIndexCollection indexes) {
|
||||
this.groupCache = groupCache;
|
||||
this.indexes = indexes;
|
||||
this.index = null;
|
||||
}
|
||||
|
||||
@AssistedInject
|
||||
GroupIndexerImpl(GroupCache groupCache,
|
||||
@Assisted GroupIndex index) {
|
||||
this.groupCache = groupCache;
|
||||
this.indexes = null;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void index(AccountGroup.UUID uuid) throws IOException {
|
||||
for (Index<?, AccountGroup> i : getWriteIndexes()) {
|
||||
i.replace(groupCache.get(uuid));
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<GroupIndex> getWriteIndexes() {
|
||||
if (indexes != null) {
|
||||
return indexes.getWriteIndexes();
|
||||
}
|
||||
|
||||
return index != null
|
||||
? Collections.singleton(index)
|
||||
: ImmutableSet.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.index.group;
|
||||
|
||||
import static com.google.gerrit.server.index.SchemaUtil.schema;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.Schema;
|
||||
import com.google.gerrit.server.index.SchemaDefinitions;
|
||||
|
||||
public class GroupSchemaDefinitions extends SchemaDefinitions<AccountGroup> {
|
||||
static final Schema<AccountGroup> V1 = schema(
|
||||
GroupField.ID,
|
||||
GroupField.UUID,
|
||||
GroupField.OWNER_UUID,
|
||||
GroupField.NAME,
|
||||
GroupField.NAME_PART,
|
||||
GroupField.DESCRIPTION,
|
||||
GroupField.IS_VISIBLE_TO_ALL);
|
||||
|
||||
public static final GroupSchemaDefinitions INSTANCE =
|
||||
new GroupSchemaDefinitions();
|
||||
|
||||
private GroupSchemaDefinitions() {
|
||||
super("groups", AccountGroup.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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.index.group;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.Index;
|
||||
import com.google.gerrit.server.index.IndexedQuery;
|
||||
import com.google.gerrit.server.index.QueryOptions;
|
||||
import com.google.gerrit.server.query.DataSource;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
|
||||
public class IndexedGroupQuery
|
||||
extends IndexedQuery<AccountGroup.UUID, AccountGroup>
|
||||
implements DataSource<AccountGroup> {
|
||||
|
||||
public IndexedGroupQuery(Index<AccountGroup.UUID, AccountGroup> index,
|
||||
Predicate<AccountGroup> pred, QueryOptions opts)
|
||||
throws QueryParseException {
|
||||
super(index, pred, opts.convertForBackend());
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,23 @@
|
||||
|
||||
package com.google.gerrit.server.query;
|
||||
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.query.change.SingleGroupUser;
|
||||
|
||||
public abstract class IsVisibleToPredicate<T> extends OperatorPredicate<T>
|
||||
implements Matchable<T> {
|
||||
public IsVisibleToPredicate(String name, String value) {
|
||||
super(name, value);
|
||||
}
|
||||
|
||||
protected static String describe(CurrentUser user) {
|
||||
if (user.isIdentifiedUser()) {
|
||||
return user.getAccountId().toString();
|
||||
}
|
||||
if (user instanceof SingleGroupUser) {
|
||||
return "group:" + user.getEffectiveGroups()
|
||||
.getKnownGroups().iterator().next().toString();
|
||||
}
|
||||
return user.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,26 +14,13 @@
|
||||
|
||||
package com.google.gerrit.server.query.account;
|
||||
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.AccountControl;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.query.IsVisibleToPredicate;
|
||||
import com.google.gerrit.server.query.change.SingleGroupUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
|
||||
public class AccountIsVisibleToPredicate
|
||||
extends IsVisibleToPredicate<AccountState> {
|
||||
private static String describe(CurrentUser user) {
|
||||
if (user.isIdentifiedUser()) {
|
||||
return user.getAccountId().toString();
|
||||
}
|
||||
if (user instanceof SingleGroupUser) {
|
||||
return "group:" + user.getEffectiveGroups().getKnownGroups() //
|
||||
.iterator().next().toString();
|
||||
}
|
||||
return user.toString();
|
||||
}
|
||||
|
||||
private final AccountControl accountControl;
|
||||
|
||||
AccountIsVisibleToPredicate(AccountControl accountControl) {
|
||||
|
||||
@@ -25,17 +25,6 @@ import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData> {
|
||||
private static String describe(CurrentUser user) {
|
||||
if (user.isIdentifiedUser()) {
|
||||
return user.getAccountId().toString();
|
||||
}
|
||||
if (user instanceof SingleGroupUser) {
|
||||
return "group:" + user.getEffectiveGroups().getKnownGroups() //
|
||||
.iterator().next().toString();
|
||||
}
|
||||
return user.toString();
|
||||
}
|
||||
|
||||
private final Provider<ReviewDb> db;
|
||||
private final ChangeNotes.Factory notesFactory;
|
||||
private final ChangeControl.GenericFactory changeControl;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.query.group;
|
||||
|
||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.gerrit.server.query.IsVisibleToPredicate;
|
||||
import com.google.gerrit.server.query.account.AccountQueryBuilder;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
|
||||
public class GroupIsVisibleToPredicate
|
||||
extends IsVisibleToPredicate<AccountGroup> {
|
||||
private final GroupControl.GenericFactory groupControlFactory;
|
||||
private final CurrentUser user;
|
||||
|
||||
GroupIsVisibleToPredicate(GroupControl.GenericFactory groupControlFactory,
|
||||
CurrentUser user) {
|
||||
super(AccountQueryBuilder.FIELD_VISIBLETO, describe(user));
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(AccountGroup group) throws OrmException {
|
||||
try {
|
||||
return groupControlFactory.controlFor(user, group.getGroupUUID())
|
||||
.isVisible();
|
||||
} catch (NoSuchGroupException e) {
|
||||
// Ignored
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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.query.group;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.index.FieldDef;
|
||||
import com.google.gerrit.server.index.IndexPredicate;
|
||||
import com.google.gerrit.server.index.group.GroupField;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GroupPredicates {
|
||||
public static Predicate<AccountGroup> defaultPredicate(String query) {
|
||||
// Adapt the capacity of this list when adding more default predicates.
|
||||
List<Predicate<AccountGroup>> preds = Lists.newArrayListWithCapacity(5);
|
||||
preds.add(uuid(new AccountGroup.UUID(query)));
|
||||
preds.add(name(query));
|
||||
preds.add(inname(query));
|
||||
if (!Strings.isNullOrEmpty(query)) {
|
||||
preds.add(description(query));
|
||||
}
|
||||
preds.add(owner(query));
|
||||
return Predicate.or(preds);
|
||||
}
|
||||
|
||||
public static Predicate<AccountGroup> uuid(AccountGroup.UUID uuid) {
|
||||
return new GroupPredicate(GroupField.UUID,
|
||||
GroupQueryBuilder.FIELD_UUID, uuid.get());
|
||||
}
|
||||
|
||||
public static Predicate<AccountGroup> description(String description) {
|
||||
return new GroupPredicate(GroupField.DESCRIPTION,
|
||||
GroupQueryBuilder.FIELD_DESCRIPTION, description);
|
||||
}
|
||||
|
||||
public static Predicate<AccountGroup> inname(String name) {
|
||||
return new GroupPredicate(GroupField.NAME_PART,
|
||||
GroupQueryBuilder.FIELD_INNAME, name.toLowerCase(Locale.US));
|
||||
}
|
||||
|
||||
public static Predicate<AccountGroup> name(String name) {
|
||||
return new GroupPredicate(GroupField.NAME,
|
||||
GroupQueryBuilder.FIELD_NAME, name.toLowerCase(Locale.US));
|
||||
}
|
||||
|
||||
public static Predicate<AccountGroup> owner(String owner) {
|
||||
return new GroupPredicate(GroupField.OWNER_UUID,
|
||||
GroupQueryBuilder.FIELD_OWNER, owner);
|
||||
}
|
||||
|
||||
public static Predicate<AccountGroup> isVisibleToAll() {
|
||||
return new GroupPredicate(GroupField.IS_VISIBLE_TO_ALL, "1");
|
||||
}
|
||||
|
||||
static class GroupPredicate extends IndexPredicate<AccountGroup> {
|
||||
GroupPredicate(FieldDef<AccountGroup, ?> def, String value) {
|
||||
super(def, value);
|
||||
}
|
||||
|
||||
GroupPredicate(FieldDef<AccountGroup, ?> def, String name, String value) {
|
||||
super(def, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private GroupPredicates() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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.query.group;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.query.LimitPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryBuilder;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
* Parses a query string meant to be applied to group objects.
|
||||
*/
|
||||
public class GroupQueryBuilder extends QueryBuilder<AccountGroup> {
|
||||
public static final String FIELD_UUID = "uuid";
|
||||
public static final String FIELD_DESCRIPTION = "description";
|
||||
public static final String FIELD_INNAME = "inname";
|
||||
public static final String FIELD_NAME = "name";
|
||||
public static final String FIELD_OWNER = "owner";
|
||||
public static final String FIELD_LIMIT = "limit";
|
||||
|
||||
private static final QueryBuilder.Definition<AccountGroup, GroupQueryBuilder> mydef =
|
||||
new QueryBuilder.Definition<>(GroupQueryBuilder.class);
|
||||
|
||||
@Inject
|
||||
GroupQueryBuilder() {
|
||||
super(mydef);
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> uuid(String uuid) {
|
||||
return GroupPredicates.uuid(new AccountGroup.UUID(uuid));
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> description(String description)
|
||||
throws QueryParseException {
|
||||
if (Strings.isNullOrEmpty(description)) {
|
||||
throw error("description operator requires a value");
|
||||
}
|
||||
|
||||
return GroupPredicates.description(description);
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> inname(String namePart) {
|
||||
if (namePart.isEmpty()) {
|
||||
return name(namePart);
|
||||
}
|
||||
return GroupPredicates.inname(namePart);
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> name(String name) {
|
||||
return GroupPredicates.name(name);
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> owner(String owner) {
|
||||
return GroupPredicates.owner(owner);
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> is(String value) throws QueryParseException {
|
||||
if ("visibleToAll".equalsIgnoreCase(value)) {
|
||||
return GroupPredicates.isVisibleToAll();
|
||||
}
|
||||
throw error("Invalid query");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate<AccountGroup> defaultField(String query) {
|
||||
return GroupPredicates.defaultPredicate(query);
|
||||
}
|
||||
|
||||
@Operator
|
||||
public Predicate<AccountGroup> limit(String query)
|
||||
throws QueryParseException {
|
||||
Integer limit = Ints.tryParse(query);
|
||||
if (limit == null) {
|
||||
throw error("Invalid limit: " + query);
|
||||
}
|
||||
return new LimitPredicate<>(FIELD_LIMIT, limit);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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.query.group;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.gerrit.server.query.group.GroupQueryBuilder.FIELD_LIMIT;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.gerrit.server.index.IndexConfig;
|
||||
import com.google.gerrit.server.index.IndexPredicate;
|
||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||
import com.google.gerrit.server.index.group.GroupIndexRewriter;
|
||||
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
|
||||
import com.google.gerrit.server.query.AndSource;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryProcessor;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
public class GroupQueryProcessor extends QueryProcessor<AccountGroup> {
|
||||
private final GroupControl.GenericFactory groupControlFactory;
|
||||
|
||||
static {
|
||||
// It is assumed that basic rewrites do not touch visibleto predicates.
|
||||
checkState(
|
||||
!GroupIsVisibleToPredicate.class.isAssignableFrom(IndexPredicate.class),
|
||||
"GroupQueryProcessor assumes visibleto is not used by the index rewriter.");
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected GroupQueryProcessor(Provider<CurrentUser> userProvider,
|
||||
Metrics metrics,
|
||||
IndexConfig indexConfig,
|
||||
GroupIndexCollection indexes,
|
||||
GroupIndexRewriter rewriter,
|
||||
GroupControl.GenericFactory groupControlFactory) {
|
||||
super(userProvider, metrics, GroupSchemaDefinitions.INSTANCE, indexConfig,
|
||||
indexes, rewriter, FIELD_LIMIT);
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate<AccountGroup> enforceVisibility(
|
||||
Predicate<AccountGroup> pred) {
|
||||
return new AndSource<>(pred,
|
||||
new GroupIsVisibleToPredicate(groupControlFactory, userProvider.get()),
|
||||
start);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.GroupUUID;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.index.group.GroupIndexCollection;
|
||||
import com.google.gwtorm.jdbc.JdbcExecutor;
|
||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
@@ -46,6 +47,7 @@ public class SchemaCreator {
|
||||
private final AllUsersCreator allUsersCreator;
|
||||
private final PersonIdent serverUser;
|
||||
private final DataSourceType dataSourceType;
|
||||
private final GroupIndexCollection indexCollection;
|
||||
|
||||
private AccountGroup admin;
|
||||
private AccountGroup batch;
|
||||
@@ -55,20 +57,23 @@ public class SchemaCreator {
|
||||
AllProjectsCreator ap,
|
||||
AllUsersCreator auc,
|
||||
@GerritPersonIdent PersonIdent au,
|
||||
DataSourceType dst) {
|
||||
this(site.site_path, ap, auc, au, dst);
|
||||
DataSourceType dst,
|
||||
GroupIndexCollection ic) {
|
||||
this(site.site_path, ap, auc, au, dst, ic);
|
||||
}
|
||||
|
||||
public SchemaCreator(@SitePath Path site,
|
||||
AllProjectsCreator ap,
|
||||
AllUsersCreator auc,
|
||||
@GerritPersonIdent PersonIdent au,
|
||||
DataSourceType dst) {
|
||||
DataSourceType dst,
|
||||
GroupIndexCollection ic) {
|
||||
site_path = site;
|
||||
allProjectsCreator = ap;
|
||||
allUsersCreator = auc;
|
||||
serverUser = au;
|
||||
dataSourceType = dst;
|
||||
indexCollection = ic;
|
||||
}
|
||||
|
||||
public void create(final ReviewDb db) throws OrmException, IOException,
|
||||
@@ -82,6 +87,7 @@ public class SchemaCreator {
|
||||
sVer.versionNbr = SchemaVersion.getBinaryVersion();
|
||||
db.schemaVersion().insert(Collections.singleton(sVer));
|
||||
|
||||
createDefaultGroups(db);
|
||||
initSystemConfig(db);
|
||||
allProjectsCreator
|
||||
.setAdministrators(GroupReference.forGroup(admin))
|
||||
@@ -93,6 +99,30 @@ public class SchemaCreator {
|
||||
dataSourceType.getIndexScript().run(db);
|
||||
}
|
||||
|
||||
private void createDefaultGroups(ReviewDb db)
|
||||
throws OrmException, IOException {
|
||||
admin = newGroup(db, "Administrators", null);
|
||||
admin.setDescription("Gerrit Site Administrators");
|
||||
db.accountGroups().insert(Collections.singleton(admin));
|
||||
db.accountGroupNames()
|
||||
.insert(Collections.singleton(new AccountGroupName(admin)));
|
||||
index(admin);
|
||||
|
||||
batch = newGroup(db, "Non-Interactive Users", null);
|
||||
batch.setDescription("Users who perform batch actions on Gerrit");
|
||||
batch.setOwnerGroupUUID(admin.getGroupUUID());
|
||||
db.accountGroups().insert(Collections.singleton(batch));
|
||||
db.accountGroupNames()
|
||||
.insert(Collections.singleton(new AccountGroupName(batch)));
|
||||
index(batch);
|
||||
}
|
||||
|
||||
private void index(AccountGroup group) throws IOException {
|
||||
if (indexCollection.getSearchIndex() != null) {
|
||||
indexCollection.getSearchIndex().replace(group);
|
||||
}
|
||||
}
|
||||
|
||||
private AccountGroup newGroup(ReviewDb c, String name, AccountGroup.UUID uuid)
|
||||
throws OrmException {
|
||||
if (uuid == null) {
|
||||
@@ -104,27 +134,14 @@ public class SchemaCreator {
|
||||
uuid);
|
||||
}
|
||||
|
||||
private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException {
|
||||
admin = newGroup(c, "Administrators", null);
|
||||
admin.setDescription("Gerrit Site Administrators");
|
||||
c.accountGroups().insert(Collections.singleton(admin));
|
||||
c.accountGroupNames().insert(
|
||||
Collections.singleton(new AccountGroupName(admin)));
|
||||
|
||||
batch = newGroup(c, "Non-Interactive Users", null);
|
||||
batch.setDescription("Users who perform batch actions on Gerrit");
|
||||
batch.setOwnerGroupUUID(admin.getGroupUUID());
|
||||
c.accountGroups().insert(Collections.singleton(batch));
|
||||
c.accountGroupNames().insert(
|
||||
Collections.singleton(new AccountGroupName(batch)));
|
||||
|
||||
final SystemConfig s = SystemConfig.create();
|
||||
private SystemConfig initSystemConfig(ReviewDb db) throws OrmException {
|
||||
SystemConfig s = SystemConfig.create();
|
||||
try {
|
||||
s.sitePath = site_path.toRealPath().normalize().toString();
|
||||
} catch (IOException e) {
|
||||
s.sitePath = site_path.toAbsolutePath().normalize().toString();
|
||||
}
|
||||
c.systemConfig().insert(Collections.singleton(s));
|
||||
db.systemConfig().insert(Collections.singleton(s));
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user