diff --git a/src/main/java/org/gridsuite/directory/server/SupervisionController.java b/src/main/java/org/gridsuite/directory/server/SupervisionController.java index dc2568e9..7a216200 100644 --- a/src/main/java/org/gridsuite/directory/server/SupervisionController.java +++ b/src/main/java/org/gridsuite/directory/server/SupervisionController.java @@ -12,17 +12,19 @@ import io.swagger.v3.oas.annotations.tags.Tag; import org.gridsuite.directory.server.dto.ElementAttributes; import org.gridsuite.directory.server.services.SupervisionService; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.Optional; import java.util.UUID; /** * @author Kevin Le Saulnier */ @RestController -@RequestMapping(value = "/" + DirectoryApi.API_VERSION + "/supervision") +@RequestMapping(value = "/" + DirectoryApi.API_VERSION + "/supervision", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @Tag(name = "directory-server - Supervision") public class SupervisionController { private final SupervisionService service; @@ -47,4 +49,17 @@ public ResponseEntity deleteElements(@RequestParam("ids") List eleme service.deleteElementsByIds(elementsUuid); return ResponseEntity.ok().build(); } + + @PostMapping(value = "/elements/recreate-index", produces = MediaType.TEXT_PLAIN_VALUE) + @Operation(summary = "Recreate the index then reindex data") + @ApiResponse(responseCode = "200", description = "Success of the index recreation & reindexing of elements") + @ApiResponse(responseCode = "500", description = "An error happen while recreating the index.\nAn manual intervention is needing as no details of the error is available.") + public ResponseEntity> deleteElements() { + if (service.recreateIndexDirectoryElementInfos()) { + return ResponseEntity.ok().build(); + } else { + return ResponseEntity.internalServerError().contentType(MediaType.TEXT_PLAIN) + .body(Optional.of("An error happen while re-creating the index. As no details is available an manual intervention is required.")); + } + } } diff --git a/src/main/java/org/gridsuite/directory/server/services/SupervisionService.java b/src/main/java/org/gridsuite/directory/server/services/SupervisionService.java index 20e4e532..572ce211 100644 --- a/src/main/java/org/gridsuite/directory/server/services/SupervisionService.java +++ b/src/main/java/org/gridsuite/directory/server/services/SupervisionService.java @@ -1,8 +1,12 @@ package org.gridsuite.directory.server.services; +import lombok.extern.slf4j.Slf4j; import org.gridsuite.directory.server.dto.ElementAttributes; +import org.gridsuite.directory.server.dto.elasticsearch.DirectoryElementInfos; import org.gridsuite.directory.server.repository.DirectoryElementEntity; import org.gridsuite.directory.server.repository.DirectoryElementRepository; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.IndexOperations; import org.springframework.stereotype.Service; import java.util.List; @@ -11,13 +15,17 @@ import static org.gridsuite.directory.server.dto.ElementAttributes.toElementAttributes; @Service +@Slf4j public class SupervisionService { private final DirectoryElementRepository directoryElementRepository; private final DirectoryRepositoryService repositoryService; + private final ElasticsearchTemplate esTemplate; - public SupervisionService(DirectoryRepositoryService repositoryService, DirectoryElementRepository directoryElementRepository) { + public SupervisionService(DirectoryRepositoryService repositoryService, DirectoryElementRepository directoryElementRepository, + ElasticsearchTemplate elasticsearchTemplate) { this.repositoryService = repositoryService; this.directoryElementRepository = directoryElementRepository; + this.esTemplate = elasticsearchTemplate; } public List getStashedElementsAttributes() { @@ -35,4 +43,28 @@ public void deleteElementsByIds(List uuids) { public List getStashedElements() { return directoryElementRepository.findAllByStashed(true); } + + public boolean recreateIndexDirectoryElementInfos() { + final IndexOperations idxDirectoryElementInfos = esTemplate.indexOps(DirectoryElementInfos.class); + final String idxDirectoryElementInfosName = idxDirectoryElementInfos.getIndexCoordinates().getIndexName(); + log.warn("Recreating ElasticSearch index {}", idxDirectoryElementInfosName); + if (idxDirectoryElementInfos.exists()) { + log.info("Index {} found, delete it.", idxDirectoryElementInfosName); + if (idxDirectoryElementInfos.delete()) { + log.info("Successfully delete index {}", idxDirectoryElementInfosName); + } else { + log.error("A problem seems to happen when deleting index {}...", idxDirectoryElementInfosName); + return false; + } + } + if (idxDirectoryElementInfos.createWithMapping()) { + log.info("Index {} successfully recreated!", idxDirectoryElementInfosName); + } else { + log.info("An error happen while re-creating index {}...", idxDirectoryElementInfosName); + return false; + } + log.info("Re-indexing all elements of {}", idxDirectoryElementInfosName); + repositoryService.reindexAllElements(); + return true; + } } diff --git a/src/test/java/org/gridsuite/directory/server/SupervisionTest.java b/src/test/java/org/gridsuite/directory/server/SupervisionTest.java index d34ada8f..0cfdb5cb 100644 --- a/src/test/java/org/gridsuite/directory/server/SupervisionTest.java +++ b/src/test/java/org/gridsuite/directory/server/SupervisionTest.java @@ -6,21 +6,30 @@ */ package org.gridsuite.directory.server; +import org.gridsuite.directory.server.dto.elasticsearch.DirectoryElementInfos; import org.gridsuite.directory.server.elasticsearch.DirectoryElementInfosRepository; import org.gridsuite.directory.server.repository.DirectoryElementEntity; import org.gridsuite.directory.server.repository.DirectoryElementRepository; +import org.gridsuite.directory.server.services.DirectoryRepositoryService; import org.gridsuite.directory.server.services.SupervisionService; import org.gridsuite.directory.server.utils.elasticsearch.DisableElasticsearch; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.IndexOperations; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import java.time.Instant; import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; @@ -39,6 +48,12 @@ class SupervisionTest { @MockBean DirectoryElementInfosRepository directoryElementInfosRepository; + @SpyBean + DirectoryRepositoryService repositoryService; + + @MockBean(name = "elasticsearchOperations") + ElasticsearchTemplate elasticsearchTemplate; + List expectedElements = List.of( new DirectoryElementEntity(UUID.randomUUID(), UUID.randomUUID(), "dir1", "DIRECTORY", "user1", null, Instant.now(), Instant.now(), "user1", true, Instant.now()), new DirectoryElementEntity(UUID.randomUUID(), UUID.randomUUID(), "filter1", "FILTER", "user1", null, Instant.now(), Instant.now(), "user1", true, Instant.now()), @@ -57,14 +72,52 @@ void testGetStashedElements() { void testDeleteElements() { List uuidsToDelete = List.of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()); supervisionService.deleteElementsByIds(uuidsToDelete); - + verify(repositoryService).deleteElements(uuidsToDelete); verify(directoryElementRepository, times(1)).deleteAllById(uuidsToDelete); verify(directoryElementInfosRepository, times(1)).deleteAllById(uuidsToDelete); } + @ParameterizedTest + @CsvSource(nullValues = { "null" }, value = { + "false, null, true, true", //not existant, juste recreate + "false, null, false, false", //not existant, error while creating + "true, true, true, true", //delete + create + reindex OK + "true, true, false, false", //delete, but error while creating + "true, false, null, false" //exist, but error while deleting + }) + void testReindexElements(final boolean exists, final Boolean delete, final Boolean create, final boolean result) { + final IndexOperations idxOps = mock(IndexOperations.class); + when(elasticsearchTemplate.indexOps(any(Class.class))).thenReturn(idxOps); + when(idxOps.getIndexCoordinates()).thenReturn(IndexCoordinates.of("test-mock-index")); //for logs + when(idxOps.exists()).thenReturn(exists); + if (delete != null) { + when(idxOps.delete()).thenReturn(delete); + } + if (create != null) { + when(idxOps.createWithMapping()).thenReturn(create); + } + doNothing().when(repositoryService).reindexAllElements(); //intercept call + assertThat(supervisionService.recreateIndexDirectoryElementInfos()).as("service call result").isEqualTo(result); + verify(elasticsearchTemplate).indexOps(DirectoryElementInfos.class); + verify(idxOps, atLeastOnce()).getIndexCoordinates(); + verify(idxOps).exists(); + if (delete != null) { + verify(idxOps).delete(); + } + if (create != null) { + verify(idxOps).createWithMapping(); + } + if (create == Boolean.TRUE) { + verify(repositoryService).reindexAllElements(); + } + verifyNoMoreInteractions(idxOps); + } + @AfterEach public void verifyNoMoreInteractionsMocks() { verifyNoMoreInteractions(directoryElementRepository); verifyNoMoreInteractions(directoryElementInfosRepository); + verifyNoMoreInteractions(repositoryService); + verifyNoMoreInteractions(elasticsearchTemplate); } }