From 4d0c45e31a87a1e8a6a94b38dc1e6f1577ba1fbf Mon Sep 17 00:00:00 2001 From: Nikesh kumar Date: Fri, 15 Mar 2024 13:26:00 +0530 Subject: [PATCH] feat(rest): create new endpoint to delete ModerationRequests by id. Signed-off-by: Nikesh kumar --- .../src/docs/asciidoc/moderationRequests.adoc | 12 ++++ .../ModerationRequestController.java | 44 ++++++++++++ .../Sw360ModerationRequestService.java | 67 ++++++++++++++----- .../restdocs/ModerationRequestSpecTest.java | 23 ++++++- 4 files changed, 129 insertions(+), 17 deletions(-) diff --git a/rest/resource-server/src/docs/asciidoc/moderationRequests.adoc b/rest/resource-server/src/docs/asciidoc/moderationRequests.adoc index c08d7123a0..332cc53612 100644 --- a/rest/resource-server/src/docs/asciidoc/moderationRequests.adoc +++ b/rest/resource-server/src/docs/asciidoc/moderationRequests.adoc @@ -146,3 +146,15 @@ include::{snippets}/should_document_get_moderationrequests_submission/curl-reque ===== Example response include::{snippets}/should_document_get_moderationrequests_submission/http-response.adoc[] + +[[resources-moderationRequest-delete]] +==== Delete Moderation Requests + +A `DELETE` method will delete list of moderation request. + +===== Example request +include::{snippets}/should_document_get_moderationrequests_delete/curl-request.adoc[] + +===== Example response +include::{snippets}/should_document_get_moderationrequests_delete/http-response.adoc[] + diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/ModerationRequestController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/ModerationRequestController.java index 090b4884d5..81de61b6ae 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/ModerationRequestController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/ModerationRequestController.java @@ -28,6 +28,7 @@ import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException; import org.eclipse.sw360.datahandler.thrift.ModerationState; import org.eclipse.sw360.datahandler.thrift.PaginationData; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.moderation.DocumentType; @@ -45,6 +46,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.BasePathAwareController; import org.springframework.data.rest.webmvc.RepositoryLinksResource; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; @@ -52,6 +54,7 @@ import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.HttpClientErrorException; @@ -342,4 +345,45 @@ private ResponseEntity> getModerationResponse HttpStatus status = resources == null ? HttpStatus.NO_CONTENT : HttpStatus.OK; return new ResponseEntity<>(resources, status); } + + @Operation( + summary = "Delete moderation request.", + description = "Delete delete moderation request of the service.", + tags = {"ModerationRequest"} + ) + @PreAuthorize("hasAuthority('WRITE')") + @RequestMapping(value = MODERATION_REQUEST_URL + "/delete", method = RequestMethod.DELETE) + public ResponseEntity deleteModerationRequest(HttpServletRequest request, @RequestBody List ids, + ModerationRequest moderationRequest) throws TException { + User sw360User = restControllerHelper.getSw360UserFromAuthentication(); + HttpStatus status; + List requestStatusList = new ArrayList<>(); + List incorrectIds = new ArrayList<>(); + List correctIds = new ArrayList<>(); + + for (String id : ids) { + try { + moderationRequest = sw360ModerationRequestService.getModerationRequestById(id); + RequestStatus requestStatus = sw360ModerationRequestService.deleteModerationRequestInfo(sw360User, id, + moderationRequest); + requestStatusList.add(requestStatus); + correctIds.add(id); + } catch (ResourceNotFoundException ex) { + incorrectIds.add(id); + } + } + + if (requestStatusList == null || requestStatusList.isEmpty()) { + return ResponseEntity.badRequest().body("No moderation request IDs provided."); + } + + Map response = new HashMap<>(); + if (!incorrectIds.isEmpty()) { + response.put("Incorrect moderation request Ids", incorrectIds); + } + if (!correctIds.isEmpty()) { + response.put("Delete moderation request Ids", correctIds); + } + return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); + } } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/Sw360ModerationRequestService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/Sw360ModerationRequestService.java index 34b5b2a9d2..3edc30f905 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/Sw360ModerationRequestService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/moderationrequest/Sw360ModerationRequestService.java @@ -15,18 +15,22 @@ import lombok.RequiredArgsConstructor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.thrift.TApplicationException; import org.apache.thrift.TException; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.THttpClient; import org.apache.thrift.transport.TTransportException; +import org.eclipse.sw360.datahandler.permissions.PermissionUtils; import org.eclipse.sw360.datahandler.thrift.ModerationState; import org.eclipse.sw360.datahandler.thrift.PaginationData; import org.eclipse.sw360.datahandler.thrift.RemoveModeratorRequestStatus; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationService; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -49,7 +53,7 @@ public class Sw360ModerationRequestService { private String thriftServerUrl; public static boolean isOpenModerationRequest(@NotNull ModerationRequest moderationRequest) { - return moderationRequest.getModerationState() == ModerationState.PENDING || moderationRequest.getModerationState() == ModerationState.INPROGRESS; + return moderationRequest.getModerationState() == ModerationState.PENDING || moderationRequest.getModerationState() == ModerationState.INPROGRESS || moderationRequest.getModerationState() == ModerationState.APPROVED; } private ModerationService.Iface getThriftModerationClient() throws TTransportException { @@ -65,21 +69,28 @@ private ModerationService.Iface getThriftModerationClient() throws TTransportExc * @return Moderation Request * @throws TException Appropriate exception if request does not exists or not accessible. */ - public ModerationRequest getModerationRequestById(String requestId) throws TException { - try { - return getThriftModerationClient().getModerationRequestById(requestId); - } catch (SW360Exception sw360Exp) { - if (sw360Exp.getErrorCode() == 404) { - throw new ResourceNotFoundException("Requested ModerationRequest not found"); - } else if (sw360Exp.getErrorCode() == 403) { - throw new AccessDeniedException( - "ModerationRequest or its Linked Project are restricted and / or not accessible"); - } else { - log.error("Error fetching moderation request by id: " + sw360Exp.getMessage()); - throw sw360Exp; - } - } - } + public ModerationRequest getModerationRequestById(String requestId) throws TException, TApplicationException { + ModerationRequest moderationRequest = null; + Map responseMap = new HashMap<>(); + try { + moderationRequest = getThriftModerationClient().getModerationRequestById(requestId); + } catch (TApplicationException tAppExp) { + log.error("Error fetching moderation request by id: " + tAppExp.getMessage()); + throw new ResourceNotFoundException("Requested ModerationRequest not found: " + requestId, tAppExp); + } catch (SW360Exception sw360Exp) { + if (sw360Exp.getErrorCode() == 404) { + throw new ResourceNotFoundException("Requested ModerationRequest not found: " + requestId); + } else if (sw360Exp.getErrorCode() == 403) { + throw new AccessDeniedException( + "ModerationRequest or its Linked Project are restricted and / or not accessible"); + } else { + log.error("Error fetching moderation request by id: " + sw360Exp.getMessage()); + throw sw360Exp; + } + } + + return moderationRequest; + } /** * Get paginated list of moderation requests where user is one of the @@ -253,4 +264,28 @@ public ModerationState assignRequest(@NotNull ModerationRequest request, @NotNul getThriftModerationClient().setInProgress(request.getId(), reviewer); return ModerationState.INPROGRESS; } + + public RequestStatus deleteModerationRequestInfo(@NotNull User sw360User, @NotNull String id, @NotNull ModerationRequest moderationRequest) + throws TTransportException, TException { + RequestStatus requestStatus = null; + if (PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) { + if (moderationRequest.getModerationState() == ModerationState.PENDING || + moderationRequest.getModerators().contains(sw360User.getEmail())) { + ModerationState moderationState = rejectRequest(moderationRequest, "Rejected by admin", sw360User); + if (moderationState == ModerationState.REJECTED || moderationState == ModerationState.APPROVED) { + requestStatus = getThriftModerationClient().deleteModerationRequest(id, sw360User); + } + } + } else if (!isOpenModerationRequest(moderationRequest)) { + throw new InvalidParameterException("Moderation request is not in open state."); + } else if (!moderationRequest.getModerators().contains(sw360User.getEmail())) { + throw new AccessDeniedException("User is not assigned as a moderator for the request."); + } else { + requestStatus = getThriftModerationClient().deleteModerationRequest(id, sw360User); + } + if (requestStatus == null) { + throw new IllegalStateException("Request status is null."); + } + return requestStatus; + } } diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ModerationRequestSpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ModerationRequestSpecTest.java index f4b4add45c..7c8f09cc9f 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ModerationRequestSpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ModerationRequestSpecTest.java @@ -13,6 +13,7 @@ import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.thrift.ModerationState; import org.eclipse.sw360.datahandler.thrift.PaginationData; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.Visibility; import org.eclipse.sw360.datahandler.thrift.components.ComponentType; import org.eclipse.sw360.datahandler.thrift.components.ECCStatus; @@ -52,8 +53,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -62,6 +65,7 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -84,6 +88,9 @@ public class ModerationRequestSpecTest extends TestRestDocsSpecBase { @MockBean private Sw360ModerationRequestService moderationRequestServiceMock; + @MockBean + private ModerationRequest moderationRequest; + @Before public void before() throws TException, IOException { Set moderatorList = new HashSet<>(); @@ -129,7 +136,7 @@ public void before() throws TException, IOException { project2Deletions.setProjectType(ProjectType.CUSTOMER); project2Deletions.setVisbility(Visibility.BUISNESSUNIT_AND_MODERATORS); - ModerationRequest moderationRequest = new ModerationRequest(); + moderationRequest = new ModerationRequest(); moderationRequest.setId("MR-101"); moderationRequest.setTimestamp(System.currentTimeMillis() / 1000L - 172800); moderationRequest.setDocumentId("R-101"); @@ -494,4 +501,18 @@ public void should_document_get_moderationrequests_submission() throws Exception subsectionWithPath("_links").description("<> to other resources") ))); } + + @Test + public void should_document_get_moderationrequests_delete() throws Exception { + String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword); + when(moderationRequestServiceMock.getModerationRequestById(anyString())).thenReturn(new ModerationRequest()); + when(moderationRequestServiceMock.deleteModerationRequestInfo(any(User.class), anyString(), any(ModerationRequest.class))) + .thenReturn(RequestStatus.SUCCESS); + mockMvc.perform(delete("/api/moderationrequest/delete/") + .content("[\"id1\", \"id2\"]") + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaTypes.HAL_JSON)) + .andExpect(status().isOk()); + } }