Implement reindex mechanism for search (#2104)

Adds a new button to repository settings to allow users to manually delete and re-create search indices. The actual re-indexing is happening in plugins that subscribe to the newly created event.

Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
Konstantin Schaper
2022-08-17 13:22:34 +02:00
committed by GitHub
parent e590a3ee68
commit 56ace2811b
12 changed files with 248 additions and 0 deletions

View File

@@ -24,11 +24,13 @@
package sonia.scm.api.v2.resources;
import com.github.legman.Subscribe;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.jboss.resteasy.mock.MockHttpRequest;
@@ -45,6 +47,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.NotFoundException;
import sonia.scm.PageResult;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.event.ScmEventBus;
import sonia.scm.importexport.ExportFileExtensionResolver;
import sonia.scm.importexport.ExportNotificationHandler;
import sonia.scm.importexport.ExportService;
@@ -68,6 +71,7 @@ import sonia.scm.repository.api.BundleCommandBuilder;
import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.search.ReindexRepositoryEvent;
import sonia.scm.search.SearchEngine;
import sonia.scm.user.User;
import sonia.scm.web.RestDispatcher;
@@ -97,6 +101,8 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
@@ -749,6 +755,60 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
verify(exportService).getExportInformation(repository);
}
@Test
public void shouldDispatchReindexEvent() throws URISyntaxException {
ReindexTestListener listener = new ReindexTestListener();
ScmEventBus.getInstance().register(listener);
Repository repository = createRepository("space", "repo");
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/reindex");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertNotNull(listener.event);
assertEquals(repository, listener.repository);
}
@Test
public void shouldThrowErrorWhenMissingPermissions() throws URISyntaxException {
Subject subject = mock(Subject.class);
doThrow(new AuthorizationException()).when(subject).checkPermission("repository:*:space-repo");
shiro.setSubject(subject);
ReindexTestListener listener = new ReindexTestListener();
ScmEventBus.getInstance().register(listener);
createRepository("space", "repo");
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/reindex");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(403, response.getStatus());
assertNull(listener.event);
}
private static class ReindexTestListener {
private ReindexRepositoryEvent event;
private Repository repository;
@Subscribe(async = false)
public void onEvent(ReindexRepositoryEvent event) {
this.repository = event.getRepository();
this.event = event;
}
}
private void mockRepositoryHandler(Set<Command> cmds) {
RepositoryHandler repositoryHandler = mock(RepositoryHandler.class);
RepositoryType repositoryType = mock(RepositoryType.class);

View File

@@ -408,6 +408,23 @@ public class RepositoryToRepositoryDtoMapperTest {
.isEqualTo("http://example.com/base/v2/search/searchableTypes/testspace/test");
}
@Test
public void shouldCreateReindexLink() {
Repository testRepository = createTestRepository();
RepositoryDto dto = mapper.map(testRepository);
assertThat(dto.getLinks().getLinkBy("reindex"))
.get()
.hasFieldOrPropertyWithValue("href", "http://example.com/base/v2/repositories/testspace/test/reindex");
}
@SubjectAware(username = "unpriv")
@Test
public void shouldNotCreateReindexLinkWithoutPermission() {
Repository testRepository = createTestRepository();
RepositoryDto dto = mapper.map(testRepository);
assertThat(dto.getLinks().getLinkBy("reindex")).isEmpty();
}
private ScmProtocol mockProtocol(String type, String protocol) {
return new MockScmProtocol(type, protocol);
}