Skip to content

Commit

Permalink
feat(rest): modify moderation requests
Browse files Browse the repository at this point in the history
Modify ModerationRequest being
- Accepted
- Rejected
- Assigned (In progress)
- Unassigned (Pending)

Commit is related to issue #1849

Signed-off-by: Gaurav Mishra <[email protected]>
  • Loading branch information
GMishx authored and heliocastro committed Jun 6, 2023
1 parent 42ffce8 commit 90c59ac
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 10 deletions.
39 changes: 39 additions & 0 deletions rest/resource-server/src/docs/asciidoc/moderationRequests.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,42 @@ include::{snippets}/should_document_get_moderationrequests_by_state/curl-request
===== Example response
include::{snippets}/should_document_get_moderationrequests_by_state/http-response.adoc[]

[[resources-moderationRequest-accept-reject]]
==== Accepting or rejecting a moderation request

A `PATCH` request will accept or reject the ModerationRequest, save the comment by the reviewer and send email notifications.

===== Request body
include::{snippets}/should_document_get_moderationrequests_accept/request-body.adoc[]

===== Request structure
include::{snippets}/should_document_get_moderationrequests_accept/request-fields.adoc[]

===== Response structure
include::{snippets}/should_document_get_moderationrequests_accept/response-fields.adoc[]

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

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

[[resources-moderationRequest-assign-unassign]]
==== Assign or Unassign a moderation request

A `PATCH` request will assign a ModerationRequest to the user. The request can be used to unassign the user from the ModerationRequest as well.

===== Request body
include::{snippets}/should_document_get_moderationrequests_assign/request-body.adoc[]

===== Request structure
include::{snippets}/should_document_get_moderationrequests_assign/request-fields.adoc[]

===== Response structure
include::{snippets}/should_document_get_moderationrequests_assign/response-fields.adoc[]

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

===== Example response
include::{snippets}/should_document_get_moderationrequests_assign/http-response.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.eclipse.sw360.rest.resourceserver.core.serializer.JsonProjectRelationSerializer;
import org.eclipse.sw360.rest.resourceserver.core.serializer.JsonReleaseRelationSerializer;
import org.eclipse.sw360.rest.resourceserver.moderationrequest.EmbeddedModerationRequest;
import org.eclipse.sw360.rest.resourceserver.moderationrequest.ModerationPatch;
import org.eclipse.sw360.rest.resourceserver.project.EmbeddedProject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -74,8 +75,6 @@ public Sw360Module() {
setMixInAnnotation(VulnerabilityState.class, Sw360Module.VulnerabilityStateMixin.class);
setMixInAnnotation(ReleaseVulnerabilityRelationDTO.class, Sw360Module.ReleaseVulnerabilityRelationDTOMixin.class);
setMixInAnnotation(VulnerabilityDTO.class, Sw360Module.VulnerabilityDTOMixin.class);
setMixInAnnotation(VulnerabilityState.class, Sw360Module.VulnerabilityStateMixin.class);
setMixInAnnotation(ReleaseVulnerabilityRelationDTO.class, Sw360Module.ReleaseVulnerabilityRelationDTOMixin.class);
setMixInAnnotation(VulnerabilityApiDTO.class, Sw360Module.VulnerabilityApiDTOMixin.class);
setMixInAnnotation(EccInformation.class, Sw360Module.EccInformationMixin.class);
setMixInAnnotation(EmbeddedProject.class, Sw360Module.EmbeddedProjectMixin.class);
Expand All @@ -97,6 +96,7 @@ public Sw360Module() {
setMixInAnnotation(ModerationRequest.class, Sw360Module.ModerationRequestMixin.class);
setMixInAnnotation(EmbeddedModerationRequest.class, Sw360Module.EmbeddedModerationRequestMixin.class);
setMixInAnnotation(ImportBomRequestPreparation.class, Sw360Module.ImportBomRequestPreparationMixin.class);
setMixInAnnotation(ModerationPatch.class, Sw360Module.ModerationPatchMixin.class);
}

@JsonInclude(JsonInclude.Include.NON_NULL)
Expand Down Expand Up @@ -1368,5 +1368,9 @@ static abstract class EmbeddedModerationRequestMixin extends ModerationRequestMi
})
public static abstract class ImportBomRequestPreparationMixin extends ImportBomRequestPreparation {
}

@JsonInclude(JsonInclude.Include.NON_NULL)
public static abstract class ModerationPatchMixin extends ModerationPatch {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpClientErrorException;

import java.time.Instant;

Expand Down Expand Up @@ -64,6 +65,11 @@ public ResponseEntity<ErrorMessage> handleMediaTypeNotSupportedException(HttpMed
return new ResponseEntity<>(new ErrorMessage(e, HttpStatus.UNSUPPORTED_MEDIA_TYPE), HttpStatus.UNSUPPORTED_MEDIA_TYPE);
}

@ExceptionHandler(HttpClientErrorException.class)
public ResponseEntity<ErrorMessage> handleClientError(HttpClientErrorException e) {
return new ResponseEntity<>(new ErrorMessage(e, e.getStatusCode()), e.getStatusCode());
}

@ExceptionHandler({OptimisticLockingFailureException.class, DataIntegrityViolationException.class})
public ResponseEntity<ErrorMessage> handleConflict(Exception e) {
return new ResponseEntity<>(new ErrorMessage(e, HttpStatus.CONFLICT), HttpStatus.CONFLICT);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Siemens AG, 2023. Part of the SW360 Portal Project.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* SPDX-FileCopyrightText: 2023, Siemens AG. Part of the SW360 Portal Project.
* SPDX-FileContributor: Gaurav Mishra <[email protected]>
*/

package org.eclipse.sw360.rest.resourceserver.moderationrequest;

/**
* Input for PATCH request on moderation request to accept/reject it.
*/
public class ModerationPatch {
private ModerationAction action;
private String comment;

public ModerationPatch() {
this.action = null;
this.comment = null;
}

public ModerationAction getAction() {
return action;
}

public void setAction(ModerationAction action) {
this.action = action;
}

public String getComment() {
return comment;
}

public void setComment(String comment) {
this.comment = comment;
}

/**
* Actions which can be performed on the moderation request
*/
public enum ModerationAction {
ACCEPT,
REJECT,
UNASSIGN,
ASSIGN
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import org.apache.thrift.TException;
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.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.components.Component;
import org.eclipse.sw360.datahandler.thrift.components.Release;
Expand All @@ -31,6 +31,7 @@
import org.eclipse.sw360.rest.resourceserver.core.HalResource;
import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper;
import org.eclipse.sw360.rest.resourceserver.project.Sw360ProjectService;
import org.eclipse.sw360.rest.resourceserver.release.ReleaseController;
import org.eclipse.sw360.rest.resourceserver.release.Sw360ReleaseService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -39,24 +40,30 @@
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;

import javax.servlet.http.HttpServletRequest;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import static org.eclipse.sw360.rest.resourceserver.moderationrequest.Sw360ModerationRequestService.isOpenModerationRequest;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;

@RestController
@BasePathAwareController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ModerationRequestController implements RepresentationModelProcessor<RepositoryLinksResource> {
Expand All @@ -81,12 +88,11 @@ public class ModerationRequestController implements RepresentationModelProcessor
@NonNull
private final com.fasterxml.jackson.databind.Module sw360Module;


@RequestMapping(value = MODERATION_REQUEST_URL, method = RequestMethod.GET)
public ResponseEntity<CollectionModel> getModerationRequests(
Pageable pageable, HttpServletRequest request,
@RequestParam(value = "allDetails", required = false) boolean allDetails)
throws TException, ResourceClassNotFoundException, PaginationParameterException, URISyntaxException {
throws TException, ResourceClassNotFoundException, URISyntaxException {
User sw360User = restControllerHelper.getSw360UserFromAuthentication();
List<ModerationRequest> moderationRequests = sw360ModerationRequestService.getRequestsByModerator(sw360User, pageable);
int totalCount = (int) sw360ModerationRequestService.getTotalCountOfRequests(sw360User);
Expand Down Expand Up @@ -129,7 +135,7 @@ public ResponseEntity<CollectionModel> getModerationRequestsByState(
stateOptions.add("open");
stateOptions.add("closed");
if (!stateOptions.contains(state)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST,
String.format("Invalid ModerationRequest state '%s', possible values are: %s", state, stateOptions));
}

Expand Down Expand Up @@ -195,6 +201,61 @@ public ResponseEntity<CollectionModel> getModerationRequestsByState(
return halModerationRequest;
}

@RequestMapping(value = MODERATION_REQUEST_URL + "/{id}", method = RequestMethod.PATCH)
public ResponseEntity<HalResource> updateModerationRequestById(@PathVariable String id,
@RequestBody ModerationPatch patch)
throws TException {
User sw360User = restControllerHelper.getSw360UserFromAuthentication();

ModerationRequest moderationRequest = sw360ModerationRequestService.getModerationRequestById(id);

if (!isOpenModerationRequest(moderationRequest)) {
throw new HttpClientErrorException(HttpStatus.METHOD_NOT_ALLOWED,
"Moderation request is already closed. Cannot perform operation.");
}

if (!moderationRequest.getModerators().contains(sw360User.getEmail())) {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST,
"Unable to perform operation, user not a moderator.");
}

ModerationState moderationStatus;
String requestResponse;
switch (patch.getAction()) {
case ACCEPT:
moderationStatus = sw360ModerationRequestService.acceptRequest(moderationRequest, patch.getComment(),
sw360User);
requestResponse = moderationStatus.toString();
break;
case REJECT:
moderationStatus = sw360ModerationRequestService.rejectRequest(moderationRequest, patch.getComment(),
sw360User);
requestResponse = moderationStatus.toString();
break;
case UNASSIGN:
moderationStatus = sw360ModerationRequestService.removeMeFromModerators(moderationRequest, sw360User);
requestResponse = moderationStatus.toString();
break;
case ASSIGN:
moderationStatus = sw360ModerationRequestService.assignRequest(moderationRequest, sw360User);
requestResponse = moderationStatus.toString();
break;
default:
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST,
"Action should be `" +
Arrays.asList(ModerationPatch.ModerationAction.values()) +
"`, '" + patch.getAction() + "' received.");
}
Map<String, String> responseMap = new HashMap<>();
responseMap.put("status", requestResponse);
HalResource responseResource = new HalResource(responseMap);
Link requestLink = linkTo(ReleaseController.class).slash("api" + MODERATION_REQUEST_URL).slash(id)
.withSelfRel();
responseResource.add(requestLink);

return new ResponseEntity<HalResource>(responseResource, HttpStatus.ACCEPTED);
}

@Override
public @NotNull RepositoryLinksResource process(RepositoryLinksResource resource) {
resource.add(linkTo(ModerationRequestController.class).slash("api" + MODERATION_REQUEST_URL).withRel("moderationRequests"));
Expand Down
Loading

0 comments on commit 90c59ac

Please sign in to comment.