diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java index ea05e1b3c8..12e98f0eff 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java @@ -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"; diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/resourcelists/ResourceComparatorGenerator.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/resourcelists/ResourceComparatorGenerator.java index 540af282c3..c3718566ca 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/resourcelists/ResourceComparatorGenerator.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/resourcelists/ResourceComparatorGenerator.java @@ -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 { @@ -38,6 +39,7 @@ public class ResourceComparatorGenerator { private static final Map> searchResultMap = generateSearchResultMap(); private static final Map> changeLogMap = generateChangeLogMap(); private static final Map> vDtoMap = generateVulDtoMap(); + private static final Map> vMap = generateVulMap(); private static final Map> moderationRequestMap = generateModerationRequestMap(); private static Map> generateComponentMap() { @@ -95,6 +97,14 @@ private static Map> gener return Collections.unmodifiableMap(vulDTOMap); } + private static Map> generateVulMap() { + Map> 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> generateModerationRequestMap() { Map> moderationRequestMap = new HashMap<>(); moderationRequestMap.put(ModerationRequest._Fields.TIMESTAMP, @@ -122,6 +132,8 @@ public Comparator generateComparator(String type) throws ResourceClassNotFoun return (Comparator)defaultChangeLogComparator(); case SW360Constants.TYPE_VULNERABILITYDTO: return (Comparator)defaultVulDtoComparator(); + case SW360Constants.TYPE_VULNERABILITY: + return (Comparator)defaultVulComparator(); case SW360Constants.TYPE_MODERATION: return (Comparator)defaultModerationRequestComparator(); case SW360Constants.TYPE_PACKAGE: @@ -200,6 +212,15 @@ public Comparator generateComparator(String type, List properties) th } } return generateVulDTOComparatorWithFields(type, vulDtos); + case SW360Constants.TYPE_VULNERABILITY: + List 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); } @@ -268,6 +289,16 @@ public Comparator generateVulDTOComparatorWithFields(String type, List generateVulComparatorWithFields(String type, List fields) throws ResourceClassNotFoundException { + switch (type) { + case SW360Constants.TYPE_VULNERABILITY: + return (Comparator)vulnComparator(fields); + default: + throw new ResourceClassNotFoundException("No comparator for resource class with name " + type); + } + } + + private Comparator componentComparator(List fields) { Comparator comparator = Comparator.comparing(x -> true); for (Component._Fields field:fields) { @@ -352,6 +383,18 @@ private Comparator vulnDtoComparator(List vulnComparator(List fields) { + Comparator comparator = Comparator.comparing(x -> true); + for (Vulnerability._Fields field:fields) { + Comparator fieldComparator = vMap.get(field); + if(fieldComparator != null) { + comparator = comparator.thenComparing(fieldComparator); + } + } + comparator = comparator.thenComparing(defaultVulComparator()); + return comparator; + } + private Comparator defaultComponentComparator() { return componentMap.get(Component._Fields.NAME); } @@ -376,6 +419,10 @@ private Comparator defaultVulDtoComparator() { return vDtoMap.get(VulnerabilityDTO._Fields.EXTERNAL_ID); } + private Comparator defaultVulComparator() { + return vMap.get(Vulnerability._Fields.EXTERNAL_ID); + } + private Comparator defaultModerationRequestComparator() { return moderationRequestMap.get(ModerationRequest._Fields.TIMESTAMP); } diff --git a/rest/resource-server/src/docs/asciidoc/vulnerabilities.adoc b/rest/resource-server/src/docs/asciidoc/vulnerabilities.adoc index 3fc76eaa7e..1068e879b0 100644 --- a/rest/resource-server/src/docs/asciidoc/vulnerabilities.adoc +++ b/rest/resource-server/src/docs/asciidoc/vulnerabilities.adoc @@ -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[] diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/VulnerabilityController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/VulnerabilityController.java index 38877e7bbd..d67c8da684 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/VulnerabilityController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/VulnerabilityController.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -91,21 +101,32 @@ public class VulnerabilityController implements RepresentationModelProcessor>> getVulnerabilities() { + public ResponseEntity>> getVulnerabilities( + Pageable pageable, + HttpServletRequest request + ) throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException { User user = restControllerHelper.getSw360UserFromAuthentication(); List vulnerabilities = vulnerabilityService.getVulnerabilities(user); - List> vulnerabilityApiDTOResources = new ArrayList<>(); - vulnerabilities.forEach(v -> { + PaginationResult paginationResult = restControllerHelper.createPaginationResult(request, pageable, vulnerabilities, SW360Constants.TYPE_VULNERABILITY); + List> vulnResources = Lists.newArrayList(); + for (Vulnerability v: paginationResult.getResources()) { Set releaseList = getReleaseRelationsInfo(v, user); VulnerabilityApiDTO vulnerabilityApiDTO = new VulnerabilityApiDTO(); restControllerHelper.setDataVulApiDTO(vulnerabilityApiDTO, v, releaseList); - vulnerabilityApiDTOResources.add(EntityModel.of(vulnerabilityApiDTO)); + vulnResources.add(EntityModel.of(vulnerabilityApiDTO)); + } + + CollectionModel> 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> resources = CollectionModel.of(vulnerabilityApiDTOResources); - return new ResponseEntity<>(resources, HttpStatus.OK); } @RequestMapping(VULNERABILITIES_URL + "/{id}") diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/VulnerabilitySpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/VulnerabilitySpecTest.java index e4bddaa6c5..3ca09e94bf 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/VulnerabilitySpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/VulnerabilitySpecTest.java @@ -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; @@ -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 <>"), - subsectionWithPath("_links").description("<> to other resources") + subsectionWithPath("_links").description("<> 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") ))); }