diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java index 687fa0e447..2cf4cab8bd 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java @@ -1,8 +1,8 @@ /* * Copyright Siemens Healthineers GmBH, 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 + * 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 @@ -414,6 +414,18 @@ private boolean changeWouldResultInDuplicate(Package before, Package after) { return duplicates.size() > 0; } + public List searchByName(String name) { + return packageRepository.searchByName(name); + } + + public List searchByVersion(String version) { + return packageRepository.searchByVersion(version); + } + + public List searchByPackageManager(String packageManager) { + return packageRepository.searchByPackageManager(packageManager); + } + private List getPackageByNameAndVersion(String pkgName, String pkgVersion) { if (isNullEmptyOrWhitespace(pkgName)) { return Collections.emptyList(); @@ -421,7 +433,7 @@ private List getPackageByNameAndVersion(String pkgName, String pkgVersi return packageRepository.searchByNameAndVersion(pkgName, pkgVersion, true); } - private List getPackageByPurl(String purl) { + public List getPackageByPurl(String purl) { if (isNullEmptyOrWhitespace(purl)) { return Collections.emptyList(); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java index ae016087fc..d6fac977ea 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java @@ -50,6 +50,7 @@ public class PackageRepository extends DatabaseRepositoryCloudantClient private static final String BY_LICENSE_IDS = "function(doc) { if (doc.type == 'package') { if (doc.licenseIds) { emit(doc.licenseIds.join(), doc._id); } else { emit('', doc._id); } } }"; private static final String BY_PURL = "function(doc) { if (doc.type == 'package') { emit(doc.purl.trim(), doc._id) } }"; private static final String BY_PURL_LOWERCASE = "function(doc) { if (doc.type == 'package') { emit(doc.purl.toLowerCase().trim(), doc._id) } }"; + private static final String BY_VERSION = "function(doc) { if (doc.type == 'package') { emit(doc.version.trim(), doc._id) } }"; public PackageRepository(DatabaseConnectorCloudant db) { super(db, Package.class); @@ -65,6 +66,7 @@ public PackageRepository(DatabaseConnectorCloudant db) { views.put("byLicenseIds", createMapReduce(BY_LICENSE_IDS, null)); views.put("byPurl", createMapReduce(BY_PURL, null)); views.put("byPurlLowercase", createMapReduce(BY_PURL_LOWERCASE, null)); + views.put("byVersion", createMapReduce(BY_VERSION, null)); initStandardDesignDocument(views, db); } @@ -76,19 +78,19 @@ public List getPackagesByReleaseId(String id) { return queryView("byReleaseId", id); } - public List searchByName(String name, User user) { + public List searchByName(String name) { return queryView("byName", name); } - public List searchByNameLowerCase(String name, User user) { + public List searchByNameLowerCase(String name) { return queryView("byNameLowerCase", name.toLowerCase()); } - public List searchByPackageManager(String pkgType, User user) { - return queryView("byPackageManager", pkgType.toUpperCase()); + public List searchByPackageManager(String pkgType) { + return queryView("byPackageManager", pkgType.toLowerCase()); } - public List searchByCreator(String email, User user) { + public List searchByCreator(String email) { return queryView("byCreator", email); } @@ -96,6 +98,10 @@ public List searchByLicenseeId(String id) { return queryView("byLicenseIds", id); } + public List searchByVersion(String version) { + return queryView("byVersion", version); + } + public List searchByNameAndVersion(String name, String version, boolean caseInsenstive) { List packagesMatchingName; if (caseInsenstive) { diff --git a/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java b/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java index 31ffb51f79..2db47dccc1 100644 --- a/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java +++ b/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java @@ -135,4 +135,32 @@ public List searchPackagesWithFilter(String text, Map searchByName(String name) throws TException { + assertNotEmpty(name); + + return handler.searchByName(name); + } + + @Override + public List searchByPackageManager(String pkgManager) throws TException { + assertNotEmpty(pkgManager); + + return handler.searchByPackageManager(pkgManager); + } + + @Override + public List searchByVersion(String version) throws TException { + assertNotEmpty(version); + + return handler.searchByVersion(version); + } + + @Override + public List searchByPurl(String purl) throws TException { + assertNotEmpty(purl); + + return handler.getPackageByPurl(purl); + } +} diff --git a/libraries/datahandler/src/main/thrift/package.thrift b/libraries/datahandler/src/main/thrift/package.thrift index 94b30e9451..b2eedf80ef 100644 --- a/libraries/datahandler/src/main/thrift/package.thrift +++ b/libraries/datahandler/src/main/thrift/package.thrift @@ -163,5 +163,25 @@ service PackageService { * total number of packages in the DB **/ i32 getTotalPackagesCount(); + + /** + * list of packages which match the `name` + */ + list searchByName(1: string name); + + /** + * list of packages which match the `packageManager` + */ + list searchByPackageManager(1: string pkgManager); + + /** + * list of packages which match the `version` + */ + list searchByVersion(1: string version); + + /** + * list of packages which match the `purl` + */ + list searchByPurl(1: string purl); } diff --git a/rest/resource-server/src/docs/asciidoc/packages.adoc b/rest/resource-server/src/docs/asciidoc/packages.adoc index 7add456783..8fdc336653 100644 --- a/rest/resource-server/src/docs/asciidoc/packages.adoc +++ b/rest/resource-server/src/docs/asciidoc/packages.adoc @@ -71,47 +71,31 @@ include::{snippets}/should_document_get_package/http-response.adoc[] ===== Links include::{snippets}/should_document_get_package/links.adoc[] -[[resources-packages-list-by-name]] -==== List packages by name +[[resources-search-packages]] +==== Search packages -A `GET` request will list all of the service's packages by name. Use the 'exactMatch' parameter to get packages by name exactly matching the search input. - -Note : send query parameter's value in encoded format. (Reference: `https://datatracker.ietf.org/doc/html/rfc3986`) +A `GET` request will filter and list all of the service's packages by name, version, purl and packageManager. ===== Request parameter -include::{snippets}/should_document_get_packages_by_name/request-parameters.adoc[] +include::{snippets}/should_document_search_packages/request-parameters.adoc[] ===== Response structure -include::{snippets}/should_document_get_packages_by_name/response-fields.adoc[] +include::{snippets}/should_document_search_packages/response-fields.adoc[] -===== Example request -include::{snippets}/should_document_get_packages_by_name/curl-request.adoc[] +===== Example request 1 +include::{snippets}/should_document_search_packages_by_name/curl-request.adoc[] -===== Example response -include::{snippets}/should_document_get_packages_by_name/http-response.adoc[] +===== Example response 1 +include::{snippets}/should_document_search_packages_by_name/http-response.adoc[] -===== Links -include::{snippets}/should_document_get_packages_by_name/links.adoc[] +===== Example request 2 +include::{snippets}/should_document_search_packages/curl-request.adoc[] -[[resources-packages-list-by-packageManager]] -==== List packages by packageManager - -A `GET` request will list all of the service's packages by packageManager. - -===== Request parameter -include::{snippets}/should_document_get_packages_by_package_manager/request-parameters.adoc[] - -===== Response structure -include::{snippets}/should_document_get_packages_by_package_manager/response-fields.adoc[] - -===== Example request -include::{snippets}/should_document_get_packages_by_package_manager/curl-request.adoc[] - -===== Example response -include::{snippets}/should_document_get_packages_by_package_manager/http-response.adoc[] +===== Example response 2 +include::{snippets}/should_document_search_packages/http-response.adoc[] ===== Links -include::{snippets}/should_document_get_packages_by_package_manager/links.adoc[] +include::{snippets}/should_document_search_packages/links.adoc[] [[resources-packages-create]] ==== Creating a package diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/PackageController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/PackageController.java index 50ec129367..baea4d824b 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/PackageController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/PackageController.java @@ -216,7 +216,11 @@ public ResponseEntity getPackagesForUser( Pageable pageable, @Parameter(description = "The name of the package.") @RequestParam(value = "name", required = false) String name, - @Parameter(description = "Type of the package manager.") + @Parameter(description = "The version of the package.") + @RequestParam(value = "version", required = false) String version, + @Parameter(description = "The pURL of the package.") + @RequestParam(value = "purl", required = false) String purl, + @Parameter(description = "The package manager of the package.") @RequestParam(value = "packageManager", required = false) String packageManager, @Parameter(description = "Get all details of the package.") @RequestParam(value = "allDetails", required = false) boolean allDetails, @@ -227,26 +231,32 @@ public ResponseEntity getPackagesForUser( User sw360User = restControllerHelper.getSw360UserFromAuthentication(); String queryString = request.getQueryString(); Map params = restControllerHelper.parseQueryString(queryString); - boolean isSearchByName = name != null && !name.isEmpty(); - boolean isSearchByType = CommonUtils.isNotNullEmptyOrWhitespace(packageManager); + boolean isSearchByName = CommonUtils.isNotNullEmptyOrWhitespace(name); + boolean isSearchByPackageManager = CommonUtils.isNotNullEmptyOrWhitespace(packageManager); + boolean isSearchByVersion = CommonUtils.isNotNullEmptyOrWhitespace(version); + boolean isSearchByPurl = CommonUtils.isNotNullEmptyOrWhitespace(purl); boolean isNoFilter = false; List sw360Packages = new ArrayList<>(); if (isSearchByName) { - sw360Packages.addAll(packageService.searchPackage("name", params.get("name"), isExactMatch)); - } else if (isSearchByType) { + sw360Packages.addAll(packageService.searchPackageByName(params.get("name"))); + } else if (isSearchByPackageManager) { packageManager = packageManager.toUpperCase(); if (!EnumUtils.isValidEnum(PackageManager.class, packageManager)) { return new ResponseEntity("Invalid package manager type. Possible values are " +Arrays.asList(PackageManager.values()), HttpStatus.BAD_REQUEST); } - sw360Packages.addAll(packageService.searchPackage("packageManager", packageManager, isExactMatch)); + sw360Packages.addAll(packageService.searchByPackageManager(packageManager)); + } else if (isSearchByVersion) { + sw360Packages.addAll(packageService.searchPackageByVersion(params.get("version"))); + } else if (isSearchByPurl) { + sw360Packages.addAll(packageService.searchPackageByPurl(params.get("purl"))); } else { sw360Packages.addAll(packageService.getPackagesForUser()); isNoFilter = true; } - return getPackageResponse(pageable, allDetails, request, sw360User, sw360Packages, isNoFilter); + return getPackageResponse(version, purl, packageManager, pageable, allDetails, request, sw360User, sw360Packages, isNoFilter); } private Package convertToPackage(Map requestBody) { @@ -278,7 +288,7 @@ private HalResource createHalPackage(Package sw360Package, User sw360Us } @NotNull - private ResponseEntity>> getPackageResponse(Pageable pageable, + private ResponseEntity>> getPackageResponse(String version, String purl, String packageManager, Pageable pageable, boolean allDetails, HttpServletRequest request, User sw360User, List sw360Packages, boolean isNoFilter) throws ResourceClassNotFoundException, PaginationParameterException, URISyntaxException, TException { @@ -314,7 +324,11 @@ private ResponseEntity>> getPackageResponse packageResources.add(embeddedPackageResource); }; - paginationResult.getResources().stream().forEach(consumer); + paginationResult.getResources().stream() + .filter(pkg -> packageManager == null || packageManager.equals(pkg.getPackageManager().toString())) + .filter(pkg -> version == null || version.isEmpty() || version.equals(pkg.getVersion())) + .filter(pkg -> purl == null || purl.isEmpty() || purl.equals(pkg.getPurl())).forEach(consumer); + CollectionModel> resources; if (packageResources.isEmpty()) { resources = restControllerHelper.emptyPageResource(Package.class, paginationResult); diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java index e59a66e236..2ff82c7474 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java @@ -142,6 +142,26 @@ public List searchPackage(String field, String searchQuery, boolean isE return sw360PackageClient.searchPackagesWithFilter(searchQuery, queryMap); } + public List searchPackageByName(String name) throws TException { + final PackageService.Iface sw360PackageClient = getThriftPackageClient(); + return sw360PackageClient.searchByName(name); + } + + public List searchByPackageManager(String pkgManager) throws TException { + final PackageService.Iface sw360PackageClient = getThriftPackageClient(); + return sw360PackageClient.searchByPackageManager(pkgManager); + } + + public List searchPackageByVersion(String version) throws TException { + final PackageService.Iface sw360PackageClient = getThriftPackageClient(); + return sw360PackageClient.searchByVersion(version); + } + + public List searchPackageByPurl(String purl) throws TException { + final PackageService.Iface sw360PackageClient = getThriftPackageClient(); + return sw360PackageClient.searchByPurl(purl); + } + public int getTotalPackagesCounts() throws TException { PackageService.Iface sw360PackageClient = getThriftPackageClient(); return sw360PackageClient.getTotalPackagesCount(); diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/PackageSpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/PackageSpecTest.java index cbb706ae3d..95aedff1ab 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/PackageSpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/PackageSpecTest.java @@ -81,6 +81,7 @@ public class PackageSpecTest extends TestRestDocsSpecBase { private Package package1; private Package package2; + private Package package3; private Set licenseIds; @Before @@ -105,8 +106,12 @@ public void before() throws TException, IOException { licenseIds.add("MIT"); licenseIds.add("GPL"); - package1 = new Package("angular-sanitize", "1.8.2", "pkg:npm/angular-sanitize@1.8.2", CycloneDxComponentType.FRAMEWORK) + package1 = new Package() .setId("122357345") + .setName("angular-sanitize") + .setVersion("1.8.2") + .setPackageType(CycloneDxComponentType.LIBRARY) + .setPurl("pkg:npm/angular-sanitize@1.8.2") .setCreatedBy("admin@sw360.org") .setCreatedOn("2023-01-02") .setVcs("git+https://github.com/angular/angular.js.git") @@ -129,6 +134,21 @@ public void before() throws TException, IOException { .setHomepageUrl("https://github.com/microsoft/ApplicationInsights-JS#readme") .setDescription("Application Insights is an extension of Azure Monitor and provides application performance monitoring (APM) features"); + package3 = new Package() + .setId("1223573425") + .setName("angular-sanitize") + .setVersion("1.8.0") + .setPackageType(CycloneDxComponentType.LIBRARY) + .setPurl("pkg:npm/angular-sanitize@1.8.0") + .setCreatedBy("admin@sw360.org") + .setCreatedOn("2023-01-02") + .setVcs("git+https://github.com/angular/angular.js.git") + .setHomepageUrl("http://angularjs.org") + .setLicenseIds(licenseIds) + .setReleaseId(testRelease.getId()) + .setPackageManager(PackageManager.NPM) + .setDescription("Sanitizes an html string by stripping all potentially dangerous tokens."); + when(this.packageServiceMock.createPackage(any(), any())).then(invocation -> new Package(package1)); @@ -136,12 +156,18 @@ public void before() throws TException, IOException { packageList.add(package1); packageList.add(package2); + List packageListByName = new ArrayList<>(); + packageListByName.add(package1); + packageListByName.add(package3); + given(this.packageServiceMock.getPackageForUserById(eq(package1.getId()))).willReturn(package1); given(this.packageServiceMock.getPackageForUserById(eq(package2.getId()))).willReturn(package2); given(this.packageServiceMock.deletePackage(eq(package1.getId()), any())).willReturn(RequestStatus.SUCCESS); given(this.packageServiceMock.getPackagesForUser()).willReturn(packageList); - given(this.packageServiceMock.searchPackage(eq("name"), any(), eq(true))).willReturn(List.of(package1)); - given(this.packageServiceMock.searchPackage(eq("packageManager"), any(), eq(false))).willReturn(packageList); + given(this.packageServiceMock.searchPackageByName(any())).willReturn(packageListByName); + given(this.packageServiceMock.searchByPackageManager(any())).willReturn(List.of(package1)); + given(this.packageServiceMock.searchPackageByVersion(any())).willReturn(List.of(package1)); + given(this.packageServiceMock.searchPackageByPurl(any())).willReturn(List.of(package1)); given(this.packageServiceMock.getTotalPackagesCounts()).willReturn(packageList.size()); @@ -270,12 +296,14 @@ public void should_document_get_package() throws Exception { } @Test - public void should_document_get_packages_by_name() throws Exception { + public void should_document_search_packages() throws Exception { String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword); mockMvc.perform(get("/api/packages") .header("Authorization", "Bearer " + accessToken) .param("name", package1.getName()) - .param("exactMatch", "true") + .param("version", package1.getVersion()) + .param("packageManager", package1.getPackageManager().toString()) + .param("purl", package1.getPurl()) .param("page", "0") .param("page_entries", "5") .param("sort", "name,desc") @@ -284,8 +312,9 @@ public void should_document_get_packages_by_name() throws Exception { .andDo(this.documentationHandler.document( requestParameters( parameterWithName("name").description("The name of the package"), - parameterWithName("exactMatch").description("If the exactMatch parameter is set to true, " - + "packages will be fetched by name exactly matching the search input"), + parameterWithName("version").description("The version of the package"), + parameterWithName("packageManager").description("The package manager type. Possible values are: " + Arrays.asList(PackageManager.values())), + parameterWithName("purl").description("The package URL"), parameterWithName("page").description("Page of packages"), parameterWithName("page_entries").description("Amount of packages per page"), parameterWithName("sort").description("Defines order of the packages") @@ -310,11 +339,11 @@ public void should_document_get_packages_by_name() throws Exception { } @Test - public void should_document_get_packages_by_package_manager() throws Exception { + public void should_document_search_packages_by_name() throws Exception { String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword); mockMvc.perform(get("/api/packages") .header("Authorization", "Bearer " + accessToken) - .param("packageManager", PackageManager.NPM.toString()) + .param("name", package1.getName()) .param("page", "0") .param("page_entries", "5") .param("sort", "name,desc") @@ -322,7 +351,7 @@ public void should_document_get_packages_by_package_manager() throws Exception { .andExpect(status().isOk()) .andDo(this.documentationHandler.document( requestParameters( - parameterWithName("packageManager").description("Type of the package manager"), + parameterWithName("name").description("The name of the package"), parameterWithName("page").description("Page of packages"), parameterWithName("page_entries").description("Amount of packages per page"), parameterWithName("sort").description("Defines order of the packages") @@ -346,7 +375,6 @@ public void should_document_get_packages_by_package_manager() throws Exception { ))); } - @Test public void should_document_create_package() throws Exception { Map pkg = new LinkedHashMap<>();