diff --git a/src/main/java/org/gridsuite/explore/server/ExploreController.java b/src/main/java/org/gridsuite/explore/server/ExploreController.java index 691bd5c2..302bdfee 100644 --- a/src/main/java/org/gridsuite/explore/server/ExploreController.java +++ b/src/main/java/org/gridsuite/explore/server/ExploreController.java @@ -242,13 +242,13 @@ public ResponseEntity deleteElement(@PathVariable("elementUuid") UUID elem return ResponseEntity.ok().build(); } - @DeleteMapping(value = "/explore/elements/{directoryUuid}/delete-stashed") + @DeleteMapping(value = "/explore/elements/{directoryUuid}", params = "ids") @Operation(summary = "Remove directories/elements") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "directories/elements was successfully removed")}) public ResponseEntity deleteElements(@RequestParam("ids") List elementsUuid, @RequestHeader("userId") String userId, - @PathVariable String directoryUuid) { - exploreService.deleteElements(elementsUuid, userId); + @PathVariable UUID directoryUuid) { + exploreService.deleteElementsFromDirectory(elementsUuid, directoryUuid, userId); return ResponseEntity.ok().build(); } diff --git a/src/main/java/org/gridsuite/explore/server/SupervisionController.java b/src/main/java/org/gridsuite/explore/server/SupervisionController.java new file mode 100644 index 00000000..09e98e41 --- /dev/null +++ b/src/main/java/org/gridsuite/explore/server/SupervisionController.java @@ -0,0 +1,32 @@ +package org.gridsuite.explore.server; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.gridsuite.explore.server.services.SupervisionService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping(value = "/" + ExploreApi.API_VERSION + "/supervision") +@Tag(name = "explore-server - Supervision") +public class SupervisionController { + private final SupervisionService supervisionService; + + public SupervisionController(SupervisionService supervisionService) { + this.supervisionService = supervisionService; + } + + @DeleteMapping(value = "/explore/elements", params = "ids") + @Operation(summary = "Remove directories/elements") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "directories/elements was successfully removed")}) + public ResponseEntity deleteElements(@RequestParam("ids") List elementsUuid, + @RequestHeader("userId") String userId) { + supervisionService.deleteElements(elementsUuid, userId); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/org/gridsuite/explore/server/services/DirectoryService.java b/src/main/java/org/gridsuite/explore/server/services/DirectoryService.java index 3f6ff4ed..262e9048 100644 --- a/src/main/java/org/gridsuite/explore/server/services/DirectoryService.java +++ b/src/main/java/org/gridsuite/explore/server/services/DirectoryService.java @@ -97,10 +97,12 @@ public void deleteDirectoryElement(UUID elementUuid, String userId) { restTemplate.exchange(directoryServerBaseUri + path, HttpMethod.DELETE, new HttpEntity<>(headers), Void.class); } - public void deleteDirectoryElements(List elementUuids, String userId) { + public void deleteElementsFromDirectory(List elementUuids, UUID parentDirectoryUuid, String userId) { var ids = elementUuids.stream().map(UUID::toString).collect(Collectors.joining(",")); String path = UriComponentsBuilder - .fromPath(ELEMENTS_SERVER_ROOT_PATH + "?ids=" + ids) + .fromPath(ELEMENTS_SERVER_ROOT_PATH) + .queryParam("ids", ids) + .queryParam("parentDirectoryUuid", parentDirectoryUuid) .buildAndExpand() .toUriString(); HttpHeaders headers = new HttpHeaders(); @@ -149,7 +151,6 @@ public void notifyDirectoryChanged(UUID elementUuid, String userId) { private List getDirectoryElements(UUID directoryUuid, String userId) { String path = UriComponentsBuilder.fromPath(DIRECTORIES_SERVER_ROOT_PATH + "/{directoryUuid}/elements") - .queryParam("stashed", true) .buildAndExpand(directoryUuid) .toUriString(); HttpHeaders headers = new HttpHeaders(); diff --git a/src/main/java/org/gridsuite/explore/server/services/ExploreService.java b/src/main/java/org/gridsuite/explore/server/services/ExploreService.java index 4f6c4a49..e6b9b051 100644 --- a/src/main/java/org/gridsuite/explore/server/services/ExploreService.java +++ b/src/main/java/org/gridsuite/explore/server/services/ExploreService.java @@ -204,14 +204,14 @@ public void deleteElement(UUID id, String userId) { } } - public void deleteElements(List uuids, String userId) { + public void deleteElementsFromDirectory(List uuids, UUID parentDirectoryUuids, String userId) { try { uuids.forEach(id -> directoryService.deleteElement(id, userId)); // FIXME dirty fix to ignore errors and still delete the elements in the directory-server. To delete when handled properly. } catch (Exception e) { LOGGER.error(e.toString(), e); } finally { - directoryService.deleteDirectoryElements(uuids, userId); + directoryService.deleteElementsFromDirectory(uuids, parentDirectoryUuids, userId); } } diff --git a/src/main/java/org/gridsuite/explore/server/services/SupervisionService.java b/src/main/java/org/gridsuite/explore/server/services/SupervisionService.java new file mode 100644 index 00000000..fc9a5b73 --- /dev/null +++ b/src/main/java/org/gridsuite/explore/server/services/SupervisionService.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.explore.server.services; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author Kevin Le Saulnier + */ +@Service +public class SupervisionService { + private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionService.class); + private final DirectoryService directoryService; + private final String directoryServerBaseUri; + private final RestTemplate restTemplate; + + private static final String DIRECTORY_SERVER_API_VERSION = "v1"; + private static final String DELIMITER = "/"; + private static final String SUPERVISION_PATH = DELIMITER + "supervision"; + private static final String ELEMENTS_SERVER_ROOT_PATH = DELIMITER + DIRECTORY_SERVER_API_VERSION + DELIMITER + SUPERVISION_PATH + DELIMITER + + "elements"; + + public SupervisionService(DirectoryService directoryService, RestTemplate restTemplate, RemoteServicesProperties remoteServicesProperties) { + this.directoryServerBaseUri = remoteServicesProperties.getServiceUri("directory-server"); + this.directoryService = directoryService; + this.restTemplate = restTemplate; + } + + public void deleteElements(List uuids, String userId) { + uuids.forEach(id -> { + try { + directoryService.deleteElement(id, userId); + } catch (Exception e) { + // if deletion fails (element does not exist, server is down...), the process keeps proceeding to at least delete references in directory-server + // orphan elements will be deleted in a dedicated script + LOGGER.error(e.toString(), e); + } + }); + deleteDirectoryElements(uuids); + } + + // DOES NOT CHECK OWNER BEFORE DELETING + private void deleteDirectoryElements(List elementUuids) { + var ids = elementUuids.stream().map(UUID::toString).collect(Collectors.joining(",")); + String path = UriComponentsBuilder + .fromPath(ELEMENTS_SERVER_ROOT_PATH) + .queryParam("ids", ids) + .buildAndExpand() + .toUriString(); + HttpHeaders headers = new HttpHeaders(); + restTemplate.exchange(directoryServerBaseUri + path, HttpMethod.DELETE, new HttpEntity<>(headers), Void.class); + } +} diff --git a/src/test/java/org/gridsuite/explore/server/ExploreTest.java b/src/test/java/org/gridsuite/explore/server/ExploreTest.java index 9c8f688e..2f35f0b5 100644 --- a/src/test/java/org/gridsuite/explore/server/ExploreTest.java +++ b/src/test/java/org/gridsuite/explore/server/ExploreTest.java @@ -465,7 +465,7 @@ public void deleteElement(UUID elementUUid) throws Exception { public void deleteElements(List elementUuids, UUID parentUuid) throws Exception { var ids = elementUuids.stream().map(UUID::toString).collect(Collectors.joining(",")); - mockMvc.perform(delete("/v1/explore/elements/{parentUuid}/delete-stashed?ids=" + ids, parentUuid) + mockMvc.perform(delete("/v1/explore/elements/{parentUuid}?ids=" + ids, parentUuid) .header("userId", USER1)) .andExpect(status().isOk()); } diff --git a/src/test/java/org/gridsuite/explore/server/SupervisionTest.java b/src/test/java/org/gridsuite/explore/server/SupervisionTest.java new file mode 100644 index 00000000..07d06479 --- /dev/null +++ b/src/test/java/org/gridsuite/explore/server/SupervisionTest.java @@ -0,0 +1,69 @@ +package org.gridsuite.explore.server; + +import org.gridsuite.explore.server.dto.ElementAttributes; +import org.gridsuite.explore.server.services.DirectoryService; +import org.gridsuite.explore.server.services.SupervisionService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +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.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.UUID; + +import static org.mockito.Mockito.*; + +/** + * @author Kevin Le Saulnier + */ +@SpringBootTest +class SupervisionTest { + @Autowired + SupervisionService supervisionService; + + @MockBean + DirectoryService directoryService; + + @MockBean + RestTemplate restTemplate; + + ElementAttributes filter = new ElementAttributes(UUID.randomUUID(), "filter", "FILTER", null, "userId", 0L, null, null); + + ElementAttributes study = new ElementAttributes(UUID.randomUUID(), "study", "STUDY", null, "userId", 0L, null, null); + + @Test + void testDeleteElements() { + List uuidsToDelete = List.of(filter.getElementUuid(), study.getElementUuid()); + supervisionService.deleteElements(uuidsToDelete, "userId"); + // deletions of both elements with foreach towards respective microservice + verify(directoryService, times(1)).deleteElement(filter.getElementUuid(), "userId"); + verify(directoryService, times(1)).deleteElement(study.getElementUuid(), "userId"); + // deletions of both elements in directory server + verify(restTemplate, times(1)).exchange(matches(".*/supervision/.*"), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)); + } + + @Test + void testDeleteElementsWithErrors() { + List uuidsToDelete = List.of(filter.getElementUuid(), study.getElementUuid()); + + // one deletion will fail, this test checks deletions does not stop even when one of them is throwing an exception + doThrow(new RuntimeException("An error occured when deleting filter")).when(directoryService).deleteElement(filter.getElementUuid(), "userId"); + + supervisionService.deleteElements(uuidsToDelete, "userId"); + // deletions of both elements with foreach towards respective microservice + verify(directoryService, times(1)).deleteElement(filter.getElementUuid(), "userId"); + verify(directoryService, times(1)).deleteElement(study.getElementUuid(), "userId"); + // deletions of both elements in directory server + verify(restTemplate, times(1)).exchange(matches(".*/supervision/.*"), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)); + } + + @AfterEach + public void verifyNoMoreInteractionsMocks() { + verifyNoMoreInteractions(restTemplate); + verifyNoMoreInteractions(directoryService); + } +}