Skip to content

Commit

Permalink
Merge pull request #2166 from siemens/feat/PaginationForVuln
Browse files Browse the repository at this point in the history
fix(rest) : adding pagination for listing vulnerabilities endpoint

Reviewed by: [email protected]
Tested by: [email protected]
  • Loading branch information
ag4ums authored Nov 16, 2023
2 parents 83dae9f + 2c8d020 commit 471856d
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public class SW360Constants {
public static final String TYPE_SEARCHRESULT = "searchResult";
public static final String TYPE_CHANGELOG = "changeLog";
public static final String TYPE_VULNERABILITYDTO = "vulDTO";
public static final String TYPE_VULNERABILITY = "vul";
public static final String TYPE_OBLIGATIONELEMENT = "obligationElement";
public static final String TYPE_OBLIGATIONNODE = "obligationNode";
public static final String TYPE_DOCUMENT = "document";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.sw360.datahandler.thrift.projects.Project;
import org.eclipse.sw360.datahandler.thrift.search.SearchResult;
import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityDTO;
import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability;

public class ResourceComparatorGenerator<T> {

Expand All @@ -38,6 +39,7 @@ public class ResourceComparatorGenerator<T> {
private static final Map<SearchResult._Fields, Comparator<SearchResult>> searchResultMap = generateSearchResultMap();
private static final Map<ChangeLogs._Fields, Comparator<ChangeLogs>> changeLogMap = generateChangeLogMap();
private static final Map<VulnerabilityDTO._Fields, Comparator<VulnerabilityDTO>> vDtoMap = generateVulDtoMap();
private static final Map<Vulnerability._Fields, Comparator<Vulnerability>> vMap = generateVulMap();
private static final Map<ModerationRequest._Fields, Comparator<ModerationRequest>> moderationRequestMap = generateModerationRequestMap();

private static Map<Component._Fields, Comparator<Component>> generateComponentMap() {
Expand Down Expand Up @@ -95,6 +97,14 @@ private static Map<VulnerabilityDTO._Fields, Comparator<VulnerabilityDTO>> gener
return Collections.unmodifiableMap(vulDTOMap);
}

private static Map<Vulnerability._Fields, Comparator<Vulnerability>> generateVulMap() {
Map<Vulnerability._Fields, Comparator<Vulnerability>> vulMap = new HashMap<>();
vulMap.put(Vulnerability._Fields.EXTERNAL_ID, Comparator.comparing(Vulnerability::getExternalId, Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER)));
vulMap.put(Vulnerability._Fields.TITLE, Comparator.comparing(Vulnerability::getTitle, Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER)));
vulMap.put(Vulnerability._Fields.PRIORITY, Comparator.comparing(Vulnerability::getPriority, Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER)));
return Collections.unmodifiableMap(vulMap);
}

private static Map<ModerationRequest._Fields, Comparator<ModerationRequest>> generateModerationRequestMap() {
Map<ModerationRequest._Fields, Comparator<ModerationRequest>> moderationRequestMap = new HashMap<>();
moderationRequestMap.put(ModerationRequest._Fields.TIMESTAMP,
Expand Down Expand Up @@ -122,6 +132,8 @@ public Comparator<T> generateComparator(String type) throws ResourceClassNotFoun
return (Comparator<T>)defaultChangeLogComparator();
case SW360Constants.TYPE_VULNERABILITYDTO:
return (Comparator<T>)defaultVulDtoComparator();
case SW360Constants.TYPE_VULNERABILITY:
return (Comparator<T>)defaultVulComparator();
case SW360Constants.TYPE_MODERATION:
return (Comparator<T>)defaultModerationRequestComparator();
case SW360Constants.TYPE_PACKAGE:
Expand Down Expand Up @@ -200,6 +212,15 @@ public Comparator<T> generateComparator(String type, List<String> properties) th
}
}
return generateVulDTOComparatorWithFields(type, vulDtos);
case SW360Constants.TYPE_VULNERABILITY:
List<Vulnerability._Fields> vul = new ArrayList<>();
for(String property : properties) {
Vulnerability._Fields field = Vulnerability._Fields.findByName(property);
if (field != null) {
vul.add(field);
}
}
return generateVulComparatorWithFields(type, vul);
default:
throw new ResourceClassNotFoundException("No comparator for resource class with name " + type);
}
Expand Down Expand Up @@ -268,6 +289,16 @@ public Comparator<T> generateVulDTOComparatorWithFields(String type, List<Vulner
}
}

public Comparator<T> generateVulComparatorWithFields(String type, List<Vulnerability._Fields> fields) throws ResourceClassNotFoundException {
switch (type) {
case SW360Constants.TYPE_VULNERABILITY:
return (Comparator<T>)vulnComparator(fields);
default:
throw new ResourceClassNotFoundException("No comparator for resource class with name " + type);
}
}


private Comparator<Component> componentComparator(List<Component._Fields> fields) {
Comparator<Component> comparator = Comparator.comparing(x -> true);
for (Component._Fields field:fields) {
Expand Down Expand Up @@ -352,6 +383,18 @@ private Comparator<VulnerabilityDTO> vulnDtoComparator(List<VulnerabilityDTO._Fi
return comparator;
}

private Comparator<Vulnerability> vulnComparator(List<Vulnerability._Fields> fields) {
Comparator<Vulnerability> comparator = Comparator.comparing(x -> true);
for (Vulnerability._Fields field:fields) {
Comparator<Vulnerability> fieldComparator = vMap.get(field);
if(fieldComparator != null) {
comparator = comparator.thenComparing(fieldComparator);
}
}
comparator = comparator.thenComparing(defaultVulComparator());
return comparator;
}

private Comparator<Component> defaultComponentComparator() {
return componentMap.get(Component._Fields.NAME);
}
Expand All @@ -376,6 +419,10 @@ private Comparator<VulnerabilityDTO> defaultVulDtoComparator() {
return vDtoMap.get(VulnerabilityDTO._Fields.EXTERNAL_ID);
}

private Comparator<Vulnerability> defaultVulComparator() {
return vMap.get(Vulnerability._Fields.EXTERNAL_ID);
}

private Comparator<ModerationRequest> defaultModerationRequestComparator() {
return moderationRequestMap.get(ModerationRequest._Fields.TIMESTAMP);
}
Expand Down
3 changes: 3 additions & 0 deletions rest/resource-server/src/docs/asciidoc/vulnerabilities.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The Vulnerabilities resource is used to create and list vulnerabilities.

A `GET` request will list all of the service's vulnerabilities.

===== Request parameter
include::{snippets}/should_document_get_vulnerabilities/request-parameters.adoc[]

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -25,11 +26,17 @@
import java.util.stream.Collectors;

import javax.websocket.server.PathParam;
import javax.servlet.http.HttpServletRequest;


import org.apache.thrift.TException;
import org.eclipse.sw360.datahandler.common.CommonUtils;
import org.eclipse.sw360.datahandler.common.SW360Constants;
import org.eclipse.sw360.datahandler.common.SW360Utils;
import org.eclipse.sw360.datahandler.permissions.DocumentPermissions;
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.RequestStatus;
import org.eclipse.sw360.datahandler.thrift.SW360Exception;
import org.eclipse.sw360.datahandler.thrift.components.Release;
Expand All @@ -44,6 +51,7 @@
import org.eclipse.sw360.rest.resourceserver.core.MultiStatus;
import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper;
import org.eclipse.sw360.rest.resourceserver.release.Sw360ReleaseService;
import org.springframework.data.domain.Pageable;
import org.eclipse.sw360.rest.resourceserver.vulnerability.Sw360VulnerabilityService.VulnerabilityOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.BasePathAwareController;
Expand Down Expand Up @@ -71,7 +79,9 @@
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;

import static org.eclipse.sw360.datahandler.common.WrappedException.wrapTException;
import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;

Expand All @@ -91,21 +101,32 @@ public class VulnerabilityController implements RepresentationModelProcessor<Rep
private Sw360ReleaseService releaseService;

@RequestMapping(value = VULNERABILITIES_URL)
public ResponseEntity<CollectionModel<EntityModel<VulnerabilityApiDTO>>> getVulnerabilities() {
public ResponseEntity<CollectionModel<EntityModel<VulnerabilityApiDTO>>> getVulnerabilities(
Pageable pageable,
HttpServletRequest request
) throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException {
User user = restControllerHelper.getSw360UserFromAuthentication();
List<Vulnerability> vulnerabilities = vulnerabilityService.getVulnerabilities(user);
List<EntityModel<VulnerabilityApiDTO>> vulnerabilityApiDTOResources = new ArrayList<>();

vulnerabilities.forEach(v -> {
PaginationResult<Vulnerability> paginationResult = restControllerHelper.createPaginationResult(request, pageable, vulnerabilities, SW360Constants.TYPE_VULNERABILITY);
List<EntityModel<VulnerabilityApiDTO>> vulnResources = Lists.newArrayList();
for (Vulnerability v: paginationResult.getResources()) {
Set<Release> releaseList = getReleaseRelationsInfo(v, user);
VulnerabilityApiDTO vulnerabilityApiDTO = new VulnerabilityApiDTO();
restControllerHelper.setDataVulApiDTO(vulnerabilityApiDTO, v, releaseList);
vulnerabilityApiDTOResources.add(EntityModel.of(vulnerabilityApiDTO));
vulnResources.add(EntityModel.of(vulnerabilityApiDTO));
}

CollectionModel<EntityModel<VulnerabilityApiDTO>> resources;
if (vulnerabilities.size() == 0) {
resources = restControllerHelper.emptyPageResource(Vulnerability.class, paginationResult);
} else {
resources = restControllerHelper.generatePagesResource(paginationResult, vulnResources);
}

});
HttpStatus status = resources == null ? HttpStatus.NO_CONTENT : HttpStatus.OK;
return new ResponseEntity<>(resources, status);

CollectionModel<EntityModel<VulnerabilityApiDTO>> resources = CollectionModel.of(vulnerabilityApiDTOResources);
return new ResponseEntity<>(resources, HttpStatus.OK);
}

@RequestMapping(VULNERABILITIES_URL + "/{id}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
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.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
Expand Down Expand Up @@ -268,17 +270,32 @@ public void should_document_get_vulnerabilities() throws Exception {
String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword);
mockMvc.perform(get("/api/vulnerabilities")
.header("Authorization", "Bearer " + accessToken)
.param("page", "0")
.param("page_entries", "5")
.param("sort", "priority,desc")
.accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
requestParameters(
parameterWithName("page").description("Page of vulnerabilities"),
parameterWithName("page_entries").description("Amount of vulnerabilities per page"),
parameterWithName("sort").description("Defines order of the vulnerabilities")
),
links(
linkWithRel("curies").description("Curies are used for online documentation")
linkWithRel("curies").description("Curies are used for online documentation"),
linkWithRel("first").description("Link to first page"),
linkWithRel("last").description("Link to last page")
),
responseFields(
subsectionWithPath("_embedded.sw360:vulnerabilityApiDTOes.[]title").description("The title of the vulnerability"),
subsectionWithPath("_embedded.sw360:vulnerabilityApiDTOes.[]externalId").description("The external Id of the vulnerability"),
subsectionWithPath("_embedded.sw360:vulnerabilityApiDTOes").description("An array of <<resources-vulnerabilities, Vulnerabilities resources>>"),
subsectionWithPath("_links").description("<<resources-index-links,Links>> to other resources")
subsectionWithPath("_links").description("<<resources-index-links,Links>> to other resources"),
fieldWithPath("page").description("Additional paging information"),
fieldWithPath("page.size").description("Number of vulnerabilities per page"),
fieldWithPath("page.totalElements").description("Total number of all existing vulnerabilities"),
fieldWithPath("page.totalPages").description("Total number of pages"),
fieldWithPath("page.number").description("Number of the current page")
)));
}

Expand Down

0 comments on commit 471856d

Please sign in to comment.