Skip to content

Commit

Permalink
Enable sorting in API (#34)
Browse files Browse the repository at this point in the history
Enable sorting for (almost) all api endpoints.

The big exception is the `/cves/{distro}/{gardenlinuxVersion}/packages/{packageList}` endpoint where implementing sorting does not work the same way because this endpoint uses a native query.

This needs more investigation to fix.
  • Loading branch information
fwilhe authored Sep 12, 2024
1 parent 76c1264 commit cb366f6
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 57 deletions.
6 changes: 5 additions & 1 deletion api-examples/Get CVEs by Gardenlinux Version.bru
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ meta {
}

get {
url: http://{{hostname}}:{{port}}/v1/cves/gardenlinux/1592.0
url: http://{{hostname}}:{{port}}/v1/cves/gardenlinux/1592.0?sortBy=cveId
body: none
auth: none
}

params:query {
sortBy: cveId
}
7 changes: 6 additions & 1 deletion api-examples/List Packages in Distro.bru
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ meta {
}

get {
url: http://{{hostname}}:{{port}}/v1/packages/distro/gardenlinux/1592.0
url: http://{{hostname}}:{{port}}/v1/packages/distro/gardenlinux/1592.0?sortBy=sourcePackageName&sortOrder=ASC
body: none
auth: none
}

params:query {
sortBy: sourcePackageName
sortOrder: ASC
}
2 changes: 1 addition & 1 deletion src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ To query all CVEs for a given distribution by version, you may use this endpoint

include::{snippets}/getCveForDistro/curl-request.adoc[]

TIP: Acceptable distributions here are `gardenlinux` and `debian`.
TIP: For all the endpoints: The `sortBy` and `sortOrder` query parameters are optional. If omitted, default sorting will be applied.

The expected response looks like this:

Expand Down
45 changes: 31 additions & 14 deletions src/main/java/io/gardenlinux/glvd/GlvdController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import jakarta.annotation.Nonnull;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.List;

Expand All @@ -25,8 +22,11 @@ public GlvdController(@Nonnull GlvdService glvdService) {

@GetMapping("/cves/{distro}/{gardenlinuxVersion}")
ResponseEntity<List<SourcePackageCve>> getCveDistro(
@PathVariable final String gardenlinuxVersion) {
return ResponseEntity.ok().body(glvdService.getCveForDistribution(gardenlinuxVersion));
@PathVariable final String gardenlinuxVersion,
@RequestParam(defaultValue = "cveId") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder
) {
return ResponseEntity.ok().body(glvdService.getCveForDistribution(gardenlinuxVersion, sortBy, sortOrder));
}

@GetMapping("/cves/{distro}/{gardenlinuxVersion}/packages/{packageList}")
Expand All @@ -37,23 +37,40 @@ ResponseEntity<List<SourcePackageCve>> getCvePackages(
}

@GetMapping("/packages/distro/{distro}/{gardenlinuxVersion}")
ResponseEntity<List<SourcePackage>> packagesForDistro(@PathVariable final String gardenlinuxVersion) {
return ResponseEntity.ok(glvdService.getPackagesForDistro(gardenlinuxVersion));
ResponseEntity<List<SourcePackage>> packagesForDistro(
@PathVariable final String gardenlinuxVersion,
@RequestParam(defaultValue = "sourcePackageName") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder
) {
return ResponseEntity.ok(glvdService.getPackagesForDistro(gardenlinuxVersion, sortBy, sortOrder));
}

@GetMapping("/packages/{sourcePackage}")
ResponseEntity<List<SourcePackageCve>> packageWithVulnerabilities(@PathVariable final String sourcePackage) {
return ResponseEntity.ok(glvdService.getPackageWithVulnerabilities(sourcePackage));
ResponseEntity<List<SourcePackageCve>> packageWithVulnerabilities(
@PathVariable final String sourcePackage,
@RequestParam(defaultValue = "cveId") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder
) {
return ResponseEntity.ok(glvdService.getPackageWithVulnerabilities(sourcePackage, sortBy, sortOrder));
}

@GetMapping("/packages/{sourcePackage}/{sourcePackageVersion}")
ResponseEntity<List<SourcePackageCve>> packageWithVulnerabilitiesByVersion(@PathVariable final String sourcePackage, @PathVariable final String sourcePackageVersion) {
return ResponseEntity.ok(glvdService.getPackageWithVulnerabilitiesByVersion(sourcePackage, sourcePackageVersion));
ResponseEntity<List<SourcePackageCve>> packageWithVulnerabilitiesByVersion(
@PathVariable final String sourcePackage,
@PathVariable final String sourcePackageVersion,
@RequestParam(defaultValue = "cveId") final String sortBy
) {
return ResponseEntity.ok(glvdService.getPackageWithVulnerabilitiesByVersion(sourcePackage, sourcePackageVersion, sortBy));
}

@GetMapping("/packages/distro/{distro}/{gardenlinuxVersion}/{cveId}")
ResponseEntity<List<SourcePackageCve>> packagesByVulnerability(@PathVariable final String gardenlinuxVersion, @PathVariable final String cveId) {
return ResponseEntity.ok(glvdService.getPackagesByVulnerability(gardenlinuxVersion, cveId));
ResponseEntity<List<SourcePackageCve>> packagesByVulnerability(
@PathVariable final String gardenlinuxVersion,
@PathVariable final String cveId,
@RequestParam(defaultValue = "cveId") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder
) {
return ResponseEntity.ok(glvdService.getPackagesByVulnerability(gardenlinuxVersion, cveId, sortBy, sortOrder));
}

}
22 changes: 12 additions & 10 deletions src/main/java/io/gardenlinux/glvd/GlvdService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.gardenlinux.glvd.db.*;
import jakarta.annotation.Nonnull;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.List;
Expand All @@ -20,27 +21,28 @@ public GlvdService(@Nonnull SourcePackageCveRepository sourcePackageCveRepositor
this.sourcePackageRepository = sourcePackageRepository;
}

public List<SourcePackageCve> getCveForDistribution(String gardenlinuxVersion) {
return sourcePackageCveRepository.findByGardenlinuxVersion(gardenlinuxVersion);
public List<SourcePackageCve> getCveForDistribution(String gardenlinuxVersion, String sortBy, String sortOrder) {
return sourcePackageCveRepository.findByGardenlinuxVersion(gardenlinuxVersion, Sort.by(Sort.Direction.valueOf(sortOrder), sortBy));
}

public List<SourcePackageCve> getCveForPackages(String gardenlinuxVersion, String packages) {
return sourcePackageCveRepository.findBySourcePackageNameInAndGardenlinuxVersion("{"+packages+"}", gardenlinuxVersion);
}

public List<SourcePackage> getPackagesForDistro(String gardenlinuxVersion) {
return sourcePackageRepository.findByGardenlinuxVersion(gardenlinuxVersion);
public List<SourcePackage> getPackagesForDistro(String gardenlinuxVersion, String sortBy, String sortOrder) {
return sourcePackageRepository.findByGardenlinuxVersion(gardenlinuxVersion, Sort.by(Sort.Direction.valueOf(sortOrder), sortBy));
}

public List<SourcePackageCve> getPackageWithVulnerabilities(String sourcePackage) {
return sourcePackageCveRepository.findBySourcePackageName(sourcePackage);
public List<SourcePackageCve> getPackageWithVulnerabilities(String sourcePackage, String sortBy, String sortOrder) {
return sourcePackageCveRepository.findBySourcePackageName(sourcePackage, Sort.by(Sort.Direction.valueOf(sortOrder), sortBy));
}

public List<SourcePackageCve> getPackageWithVulnerabilitiesByVersion(String sourcePackage, String sourcePackageVersion) {
return sourcePackageCveRepository.findBySourcePackageNameAndSourcePackageVersion(sourcePackage, sourcePackageVersion);
public List<SourcePackageCve> getPackageWithVulnerabilitiesByVersion(String sourcePackage, String sourcePackageVersion, String sortBy) {
return sourcePackageCveRepository.findBySourcePackageNameAndSourcePackageVersion(sourcePackage, sourcePackageVersion, Sort.by(Sort.Direction.DESC, sortBy));
}

public List<SourcePackageCve> getPackagesByVulnerability(String gardenlinuxVersion, String cveId) {
return sourcePackageCveRepository.findByCveIdAndGardenlinuxVersion(cveId, gardenlinuxVersion);
public List<SourcePackageCve> getPackagesByVulnerability(String gardenlinuxVersion, String cveId, String sortBy, String sortOrder) {
return sourcePackageCveRepository.findByCveIdAndGardenlinuxVersion(cveId, gardenlinuxVersion, Sort.by(Sort.Direction.valueOf(sortOrder), sortBy));
}

}
14 changes: 9 additions & 5 deletions src/main/java/io/gardenlinux/glvd/UiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ public UiController(@Nonnull GlvdService glvdService) {
@GetMapping("/getPackagesForDistro")
public String getPackagesForDistro(
@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
@RequestParam(defaultValue = "cveId") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder,
Model model) {
var packages = glvdService.getPackagesForDistro(gardenlinuxVersion);
var packages = glvdService.getPackagesForDistro(gardenlinuxVersion, sortBy, sortOrder);
model.addAttribute("packages", packages);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
return "getPackagesForDistro";
Expand All @@ -29,17 +31,18 @@ public String getPackagesForDistro(
@GetMapping("/getCveForDistribution")
public String getCveForDistribution(
@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
@RequestParam(defaultValue = "cveId") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder,
Model model
) {
var sourcePackageCves = glvdService.getCveForDistribution(gardenlinuxVersion);
var sourcePackageCves = glvdService.getCveForDistribution(gardenlinuxVersion, sortBy, sortOrder);
model.addAttribute("sourcePackageCves", sourcePackageCves);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
return "getCveForDistribution";
}

@GetMapping("/getCveForPackages")
public String getCveForPackages(

@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
@RequestParam(name = "packages", required = true) String packages,
Model model
Expand All @@ -53,12 +56,13 @@ public String getCveForPackages(

@GetMapping("/getPackagesByVulnerability")
public String getPackagesByVulnerability(

@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
@RequestParam(name = "cveId", required = true) String cveId,
@RequestParam(defaultValue = "cveId") final String sortBy,
@RequestParam(defaultValue = "ASC") final String sortOrder,
Model model
) {
var sourcePackageCves = glvdService.getPackagesByVulnerability(gardenlinuxVersion, cveId);
var sourcePackageCves = glvdService.getPackagesByVulnerability(gardenlinuxVersion, cveId, sortBy, sortOrder);
model.addAttribute("sourcePackageCves", sourcePackageCves);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
model.addAttribute("cveId", cveId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.gardenlinux.glvd.db;

import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
Expand All @@ -8,28 +10,18 @@

public interface SourcePackageCveRepository extends JpaRepository<SourcePackageCve, String> {

List<SourcePackageCve> findBySourcePackageName(@Param("source_package_name") String source_package_name);
List<SourcePackageCve> findBySourcePackageNameAndSourcePackageVersion(@Param("source_package_name") String source_package_name, @Param("source_package_version") String source_package_version);
List<SourcePackageCve> findByCveIdAndGardenlinuxVersion(@Param("cve_id") String cve_id, @Param("gardenlinux_version") String gardenlinux_version);
List<SourcePackageCve> findBySourcePackageName(@Param("source_package_name") String source_package_name, Sort sort);
List<SourcePackageCve> findBySourcePackageNameAndSourcePackageVersion(@Param("source_package_name") String source_package_name, @Param("source_package_version") String source_package_version, Sort sort);
List<SourcePackageCve> findByCveIdAndGardenlinuxVersion(@Param("cve_id") String cve_id, @Param("gardenlinux_version") String gardenlinux_version, Sort sort);

List<SourcePackageCve> findByGardenlinuxVersion(@Param("gardenlinux_version") String gardenlinux_version);
List<SourcePackageCve> findByGardenlinuxVersion(@Param("gardenlinux_version") String gardenlinux_version, Sort sort);

// would be nice if we did not need a native query here
// is this possible in any other way?
@Query(value = "select * from sourcepackagecve where source_package_name = ANY(:source_package_names ::TEXT[]) AND gardenlinux_version = :gardenlinux_version", nativeQuery = true)
List<SourcePackageCve> findBySourcePackageNameInAndGardenlinuxVersion(@Param("source_package_names") String source_package_names, @Param("gardenlinux_version") String gardenlinux_version);

// is this (the in-array search for packages) possible in any other way with spring data jpa?
// fixme: does not support sorting, cf https://github.com/spring-projects/spring-data-jpa/issues/2504#issuecomment-1527743003
@Query(value = """
SELECT
debsrc.deb_source AS source_package_name
FROM
dist_cpe
INNER JOIN debsrc ON
(debsrc.dist_id = dist_cpe.id)
WHERE
dist_cpe.cpe_product = :distro
AND dist_cpe.cpe_version = :distroVersion
ORDER BY
debsrc.deb_source""", nativeQuery = true)
List<String> packagesForDistribution(@Param("distro") String distro, @Param("distroVersion") String distroVersion);
SELECT * FROM sourcepackagecve
WHERE source_package_name = ANY(:source_package_names ::TEXT[]) AND gardenlinux_version = :gardenlinux_version
""", nativeQuery = true)
List<SourcePackageCve> findBySourcePackageNameInAndGardenlinuxVersion(@Param("source_package_names") String source_package_names, @Param("gardenlinux_version") String gardenlinux_version);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.gardenlinux.glvd.db;

import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface SourcePackageRepository extends JpaRepository<SourcePackage, String> {
List<SourcePackage> findByGardenlinuxVersion(@Param("gardenlinux_version") String gardenlinux_version);
List<SourcePackage> findByGardenlinuxVersion(@Param("gardenlinux_version") String gardenlinux_version, Sort by);
}
6 changes: 2 additions & 4 deletions src/test/java/io/gardenlinux/glvd/GlvdControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.AfterAll;
Expand All @@ -25,7 +24,6 @@
import org.testcontainers.utility.DockerImageName;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
Expand Down Expand Up @@ -76,12 +74,12 @@ void setUp(RestDocumentationContextProvider restDocumentation) {
}

@Test
public void shouldReturnCvesForBookworm() {
public void shouldReturnCvesForGardenlinux() {
given(this.spec).accept("application/json")
.filter(document("getCveForDistro",
preprocessRequest(modifyUris().scheme("https").host("glvd.gardenlinux.io").removePort()),
preprocessResponse(prettyPrint())))
.when().port(this.port).get("/v1/cves/gardenlinux/1592.0")
.when().port(this.port).get("/v1/cves/gardenlinux/1592.0?sortBy=cveId&sortOrder=DESC")
.then().statusCode(HttpStatus.SC_OK);
}

Expand Down

0 comments on commit cb366f6

Please sign in to comment.