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 <ekempin@google.com>
This commit is contained in:
		@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
 
 | 
			
		||||
@@ -143,6 +143,15 @@ public interface GroupApi {
 | 
			
		||||
   */
 | 
			
		||||
  List<? extends GroupAuditEventInfo> 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();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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()) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user