From 4550a256cbeb07cd4ba8fed8b125bd77835f0674 Mon Sep 17 00:00:00 2001 From: Edwin Kempin Date: Wed, 4 Jan 2017 14:22:02 +0100 Subject: [PATCH] Add REST endpoint to reindex a single group This may become handy to fix single groups that are stale in the index. Change-Id: Iab3711f343fe1da9bba4979792edc83c76d75a09 Signed-off-by: Edwin Kempin --- Documentation/rest-api-groups.txt | 18 ++++++ .../gerrit/acceptance/api/group/GroupsIT.java | 27 +++++++++ .../extensions/api/groups/GroupApi.java | 14 +++++ .../server/api/groups/GroupApiImpl.java | 13 ++++ .../com/google/gerrit/server/group/Index.java | 59 +++++++++++++++++++ .../google/gerrit/server/group/Module.java | 1 + .../query/group/AbstractQueryGroupsTest.java | 21 +++++++ 7 files changed, 153 insertions(+) create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt index 69a8e1f40c..bf83112fd7 100644 --- a/Documentation/rest-api-groups.txt +++ b/Documentation/rest-api-groups.txt @@ -791,6 +791,24 @@ newest audit event comes first. ] ---- +[[index-group]] +=== Index Group +-- +'POST /groups/link:#group-id[\{group-id\}]/index' +-- + +Adds or updates the internal group in the secondary index. + +.Request +---- + POST /groups/fdda826a0815859ab48d22a05a43472f0f55f89a/index HTTP/1.0 +---- + +.Response +---- + HTTP/1.1 204 No Content +---- + [[group-member-endpoints]] == Group Member Endpoints diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java index ccef13c91a..00a24fbac8 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java @@ -45,6 +45,7 @@ import org.junit.Test; import java.sql.Timestamp; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -481,6 +482,32 @@ public class GroupsIT extends AbstractDaemonTest { } } + // reindex is tested by {@link AbstractQueryGroupsTest#reindex} + @Test + public void reindexPermissions() throws Exception { + TestAccount groupOwner = accounts.user2(); + GroupInput in = new GroupInput(); + in.name = name("group"); + in.members = Collections.singleton(groupOwner).stream() + .map(u -> u.id.toString()).collect(toList()); + in.visibleToAll = true; + GroupInfo group = gApi.groups().create(in).get(); + + // admin can reindex any group + setApiUser(admin); + gApi.groups().id(group.id).index(); + + // group owner can reindex own group (group is owned by itself) + setApiUser(groupOwner); + gApi.groups().id(group.id).index(); + + // user cannot reindex any group + setApiUser(user); + exception.expect(AuthException.class); + exception.expectMessage("not allowed to index group"); + gApi.groups().id(group.id).index(); + } + private void assertAuditEvent(GroupAuditEventInfo info, Type expectedType, Account.Id expectedUser, Account.Id expectedMember) { assertThat(info.user._accountId).isEqualTo(expectedUser.get()); diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java index d3f4463cca..2c81b81184 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java @@ -143,6 +143,15 @@ public interface GroupApi { */ List auditLog() throws RestApiException; + /** + * Reindexes the group. + * + * Only supported for internal groups. + * + * @throws RestApiException + */ + void index() throws RestApiException; + /** * A default implementation which allows source compatibility * when adding new methods to the interface. @@ -239,5 +248,10 @@ public interface GroupApi { throws RestApiException { throw new NotImplementedException(); } + + @Override + public void index() { + throw new NotImplementedException(); + } } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java index 3e0aaf0aec..b939a3bf64 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java @@ -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; } @@ -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); + } + } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java new file mode 100644 index 0000000000..a1c9471cd4 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java @@ -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 { + 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(); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java index 58b3ffbb7d..f6d0453d6f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java @@ -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); diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java index c53b7c401a..8278030f3c 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java @@ -59,6 +59,7 @@ import org.junit.Test; import org.junit.rules.TestName; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -300,6 +301,26 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests { assertQuery("uuid:" + group.id); } + // reindex permissions are tested by {@link GroupsIT#reindexPermissions} + @Test + public void reindex() throws Exception { + GroupInfo group1 = createGroupWithDescription(name("group"), "barX"); + + // update group in the database so that group index is stale + String newDescription = "barY"; + AccountGroup group = + db.accountGroups().get(new AccountGroup.Id(group1.groupId)); + group.setDescription(newDescription); + db.accountGroups().update(Collections.singleton(group)); + + assertQuery("description:" + group1.description, group1); + assertQuery("description:" + newDescription); + + gApi.groups().id(group1.id).index(); + assertQuery("description:" + group1.description); + assertQuery("description:" + newDescription, group1); + } + private Account.Id createAccount(String username, String fullName, String email, boolean active) throws Exception { try (ManualRequestContext ctx = oneOffRequestContext.open()) {