Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rest): create new endpoint to delete ModerationRequests by id. #2363

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions rest/resource-server/src/docs/asciidoc/moderationRequests.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,14 @@ include::{snippets}/should_document_check_user_message_moderationrequests/curl-r

===== Example response
include::{snippets}/should_document_check_user_message_moderationrequests/http-response.adoc[]

[[resources-moderationRequest-delete]]
==== Delete Moderation Requests

A `DELETE` method will delete list of moderation request.

===== Example request
include::{snippets}/should_document_delete_moderationrequests/curl-request.adoc[]

===== Example response
include::{snippets}/should_document_delete_moderationrequests/http-response.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
import org.apache.thrift.transport.TTransportException;
import org.eclipse.sw360.datahandler.common.CommonUtils;
import org.eclipse.sw360.datahandler.common.SW360Constants;
import org.eclipse.sw360.datahandler.resourcelists.PaginationParameterException;
import org.eclipse.sw360.datahandler.permissions.PermissionUtils;
import org.eclipse.sw360.datahandler.resourcelists.PaginationResult;
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.SW360Exception;
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.licenses.License;
Expand All @@ -52,13 +55,15 @@
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;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
Expand All @@ -70,6 +75,8 @@
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;

import java.net.URISyntaxException;
import java.util.*;
import java.util.function.Function;
Expand Down Expand Up @@ -383,6 +390,7 @@ private ResponseEntity<CollectionModel<ModerationRequest>> getModerationResponse
return new ResponseEntity<>(resources, status);
}


/**
* Filter moderation request to remove duplicate additions and deletions data.
* @param moderationRequest Moderation request to filter
Expand Down Expand Up @@ -614,4 +622,75 @@ private Object getEntityByTypeAndId(String entityType, String entityId, User use
throw new RuntimeException("Unable to connect to the service. Please check the server status.", e);
}
}
@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,
@Parameter(description = "List of moderation request IDs to delete")
@RequestBody List<String> ids) throws TException {
User sw360User = restControllerHelper.getSw360UserFromAuthentication();
List<RequestStatus> requestStatusList = new ArrayList<>();
List<String> incorrectIds = new ArrayList<>();
List<String> correctIds = new ArrayList<>();
List<String> deletedIds = new ArrayList<>();

for (String id : ids) {
try {
ModerationRequest moderationRequest = sw360ModerationRequestService.getModerationRequestById(id);
RequestStatus requestStatus = sw360ModerationRequestService.deleteModerationRequestInfo(sw360User, id,
moderationRequest);
requestStatusList.add(requestStatus);
if (requestStatus == RequestStatus.SUCCESS) {
deletedIds.add(id);
} else {
correctIds.add(id);
}
} catch (ResourceNotFoundException ex) {
incorrectIds.add(id);
}
}

Map<String, Object> response = new HashMap<>();
response.put("deleted", deletedIds);
response.put("incorrect", incorrectIds);
response.put("correct", correctIds);

if (!requestStatusList.isEmpty()) {
if (requestStatusList.contains(RequestStatus.FAILURE)) {
response.put("message", "User doesn't have permission to delete.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
} else if (requestStatusList.contains(RequestStatus.SUCCESS) && requestStatusList.contains(null)) {
response.put("message", "Some requests were deleted, but some are in an open state.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
} else if (requestStatusList.contains(RequestStatus.SUCCESS) && incorrectIds.isEmpty() && correctIds.isEmpty()) {
response.put("message", "All specified moderation requests were successfully deleted.");
return ResponseEntity.status(HttpStatus.OK).body(response);
} else if (requestStatusList.contains(null)) {
response.put("message", "MR is in open state or user don't have permission.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
}
}

if (!incorrectIds.isEmpty() && !correctIds.isEmpty()) {
response.put("message", "Some moderation requests are invalid or open.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
} else if (incorrectIds.isEmpty() && correctIds.isEmpty() && !deletedIds.isEmpty()) {
response.put("message", "All specified moderation requests were successfully deleted.");
return ResponseEntity.status(HttpStatus.OK).body(response);
} else if (!incorrectIds.isEmpty()) {
response.put("message", "Some moderation requests are invalid.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
} else if (!correctIds.isEmpty()) {
response.put("message", "Some moderation requests are in an open state.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
} else {
response.put("message", "No valid moderation requests found.");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
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.common.SW360Utils;
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;
Expand All @@ -37,6 +39,7 @@
import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformationService;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.users.UserService;
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;
Expand All @@ -51,6 +54,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
Expand Down Expand Up @@ -116,22 +120,35 @@ private UserService.Iface getThriftUserClient() throws TTransportException {
* @return Moderation Request
* @throws TException Appropriate exception if request does not exists or not accessible.
*/
public ModerationRequest getModerationRequestById(String requestId) throws TException {
public ModerationRequest getModerationRequestById(String requestId) throws TException, TApplicationException {
ModerationRequest moderationRequest = null;
try {
return getThriftModerationClient().getModerationRequestById(requestId);
moderationRequest = getThriftModerationClient().getModerationRequestById(requestId);
} catch (TApplicationException tAppExp) {
log.error("TApplicationException while fetching moderation request by ID: {}. Exception: {}",
requestId, tAppExp.getMessage(), tAppExp);
throw new ResourceNotFoundException("Requested ModerationRequest not found: " + requestId, tAppExp);
} catch (SW360Exception sw360Exp) {
if (sw360Exp.getErrorCode() == 404) {
throw new ResourceNotFoundException("Requested ModerationRequest not found");
log.warn("ModerationRequest not found with ID: {}", requestId);
throw new ResourceNotFoundException("Requested ModerationRequest not found: " + requestId, sw360Exp);
} else if (sw360Exp.getErrorCode() == 403) {
throw new AccessDeniedException(
"ModerationRequest or its Linked Project are restricted and / or not accessible");
log.warn("Access denied for ModerationRequest or its linked project with ID: {}", requestId);
throw new AccessDeniedException("ModerationRequest or its Linked Project are restricted and/or not accessible", sw360Exp);
} else {
log.error("Error fetching moderation request by id: " + sw360Exp.getMessage());
log.error("Unhandled SW360Exception while fetching moderation request by ID: {}. Exception: {}",
requestId, sw360Exp.getMessage(), sw360Exp);
throw sw360Exp;
}
} catch (Exception ex) {
log.error("Unexpected exception while fetching moderation request by ID: {}. Exception: {}",
requestId, ex.getMessage(), ex);
throw new RuntimeException("An unexpected error occurred while fetching the ModerationRequest: " + requestId, ex);
}
return moderationRequest;
}


/**
* Get paginated list of moderation requests where user is one of the
* moderators.
Expand Down Expand Up @@ -447,4 +464,27 @@ public Integer getOpenCriticalCrCountByGroup(String group) {
return 0;
}
}

public RequestStatus deleteModerationRequestInfo(@NotNull User sw360User, @NotNull String id,
@NotNull ModerationRequest moderationRequest)
throws TTransportException, TException {
RequestStatus requestStatus = null;
Set<String> moderators = moderationRequest.getModerators();
String requestingUser = moderationRequest.getRequestingUser();
ModerationState moderationState = moderationRequest.getModerationState();

if (moderators.contains(sw360User.getEmail())) {
if (moderationState == ModerationState.REJECTED || moderationState == ModerationState.APPROVED) {
requestStatus = getThriftModerationClient().deleteModerationRequest(id, sw360User);
}
} else if (!sw360User.getEmail().equals(requestingUser)) {
if (moderationState == ModerationState.PENDING || moderationState == ModerationState.INPROGRESS) {
requestStatus = RequestStatus.FAILURE;
}
} else {
requestStatus = getThriftModerationClient().deleteModerationRequest(id, sw360User);
}

return requestStatus;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -66,6 +67,7 @@
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
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;
Expand All @@ -90,6 +92,7 @@ public class ModerationRequestSpecTest extends TestRestDocsSpecBase {

@MockBean
private Project project;
private ModerationRequest moderationRequest;

@Before
public void before() throws TException, IOException {
Expand Down Expand Up @@ -136,7 +139,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");
Expand Down Expand Up @@ -514,4 +517,33 @@ public void should_document_check_user_message_moderationrequests() throws Excep
.andExpect(status().isOk())
.andReturn();
}

@Test
public void should_document_delete_moderationrequests() throws Exception {
String accessToken = TestHelper.generateAuthHeader(testUserId, testUserPassword);
ModerationRequest mr3 = new ModerationRequest();
mr3.setId("MR-20443");
mr3.setRevision("1");
mr3.setTimestamp(System.currentTimeMillis() / 1000L - 172800);
mr3.setTimestampOfDecision(System.currentTimeMillis() / 1000L - 155000);
mr3.setDocumentId("P-102");
mr3.setDocumentType(DocumentType.PROJECT);
mr3.setRequestingUser("[email protected]");
mr3.setDocumentName("Project 2");
mr3.setModerationState(ModerationState.REJECTED);
mr3.setReviewer("[email protected]");
mr3.setRequestingUserDepartment("DEPT");
mr3.setComponentType(ComponentType.OSS);
mr3.setCommentRequestingUser("Update project version");

given(this.moderationRequestServiceMock.deleteModerationRequestInfo(any(), any(), any()))
.willReturn(RequestStatus.SUCCESS);

mockMvc.perform(delete("/api/moderationrequest/delete")
.content("[\"" + mr3.getId() + "\"]")
.header("Authorization", accessToken)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk());
}
}
Loading