Skip to content

Commit

Permalink
Merge pull request #1030 from siemens/feat/ProjectAndSearchEndpoint
Browse files Browse the repository at this point in the history
feat(rest): New search resource endpoint and get releases for multiple projects

review-by: [email protected]
tested-by: [email protected]
  • Loading branch information
JaideepPalit authored Nov 6, 2020
2 parents b58f3ca + 693dc59 commit 77730ce
Show file tree
Hide file tree
Showing 17 changed files with 658 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public class SW360Constants {
public static final String TYPE_PROJECT_OBLIGATION = "obligationList";
public static final String TYPE_MODERATION = "moderation";
public static final String TYPE_CLEARING = "clearing";
public static final String TYPE_SEARCHRESULT = "searchResult";

/**
* Hashmap containing the name field for each type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.eclipse.sw360.datahandler.thrift.components.Component;
import org.eclipse.sw360.datahandler.thrift.components.Release;
import org.eclipse.sw360.datahandler.thrift.projects.Project;
import org.eclipse.sw360.datahandler.thrift.search.SearchResult;

import java.util.*;

Expand All @@ -25,6 +26,7 @@ public class ResourceComparatorGenerator<T> {
private static final Map<Component._Fields, Comparator<Component>> componentMap = generateComponentMap();
private static final Map<Project._Fields, Comparator<Project>> projectMap = generateProjectMap();
private static final Map<Release._Fields, Comparator<Release>> releaseMap = generateReleaseMap();
private static final Map<SearchResult._Fields, Comparator<SearchResult>> searchResultMap = generateSearchResultMap();

private static Map<Component._Fields, Comparator<Component>> generateComponentMap() {
Map<Component._Fields, Comparator<Component>> componentMap = new HashMap<>();
Expand Down Expand Up @@ -52,6 +54,13 @@ private static Map<Release._Fields, Comparator<Release>> generateReleaseMap() {
return Collections.unmodifiableMap(releaseMap);
}

private static Map<SearchResult._Fields, Comparator<SearchResult>> generateSearchResultMap() {
Map<SearchResult._Fields, Comparator<SearchResult>> searchResultMap = new HashMap<>();
searchResultMap.put(SearchResult._Fields.NAME, Comparator.comparing(SearchResult::getName, Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER)));
searchResultMap.put(SearchResult._Fields.TYPE, Comparator.comparing(SearchResult::getType, Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER)));
return Collections.unmodifiableMap(searchResultMap);
}

public Comparator<T> generateComparator(String type) throws ResourceClassNotFoundException {
switch (type) {
case SW360Constants.TYPE_COMPONENT:
Expand All @@ -60,6 +69,8 @@ public Comparator<T> generateComparator(String type) throws ResourceClassNotFoun
return (Comparator<T>)defaultProjectComparator();
case SW360Constants.TYPE_RELEASE:
return (Comparator<T>)defaultReleaseComparator();
case SW360Constants.TYPE_SEARCHRESULT:
return (Comparator<T>)defaultSearchResultComparator();
default:
throw new ResourceClassNotFoundException("No default comparator for resource class with name " + type);
}
Expand Down Expand Up @@ -98,6 +109,15 @@ public Comparator<T> generateComparator(String type, List<String> properties) th
}
}
return generateReleaseComparatorWithFields(type, releaeFields);
case SW360Constants.TYPE_SEARCHRESULT:
List<SearchResult._Fields> searchReult = new ArrayList<>();
for(String property:properties) {
SearchResult._Fields field = SearchResult._Fields.findByName(property);
if (field != null) {
searchReult.add(field);
}
}
return generateSearchResultComparatorWithFields(type, searchReult);
default:
throw new ResourceClassNotFoundException("No comparator for resource class with name " + type);
}
Expand Down Expand Up @@ -130,6 +150,15 @@ public Comparator<T> generateReleaseComparatorWithFields(String type, List<Relea
}
}

public Comparator<T> generateSearchResultComparatorWithFields(String type, List<SearchResult._Fields> fields) throws ResourceClassNotFoundException {
switch (type) {
case SW360Constants.TYPE_SEARCHRESULT:
return (Comparator<T>)searchResultComparator(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 @@ -166,10 +195,22 @@ private Comparator<Release> releaseComparator(List<Release._Fields> fields) {
return comparator;
}

private Comparator<SearchResult> searchResultComparator(List<SearchResult._Fields> fields) {
Comparator<SearchResult> comparator = Comparator.comparing(x -> true);
for (SearchResult._Fields field:fields) {
Comparator<SearchResult> fieldComparator = searchResultMap.get(field);
if(fieldComparator != null) {
comparator = comparator.thenComparing(fieldComparator);
}
}
comparator = comparator.thenComparing(defaultSearchResultComparator());
return comparator;
}

private Comparator<Component> defaultComponentComparator() {
return componentMap.get(Component._Fields.NAME);
}

private Comparator<Project> defaultProjectComparator() {
return projectMap.get(Project._Fields.NAME);
}
Expand All @@ -178,4 +219,8 @@ private Comparator<Release> defaultReleaseComparator() {
return releaseMap.get(Release._Fields.NAME);
}

private Comparator<SearchResult> defaultSearchResultComparator() {
return searchResultMap.get(SearchResult._Fields.NAME);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.sw360.datahandler.thrift.projects.Project;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectType;
import org.eclipse.sw360.datahandler.thrift.search.SearchResult;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -138,4 +139,22 @@ public void checkReleaseComparatorName() throws ResourceClassNotFoundException {
assertTrue(nameComparator.compare(new Release().setName("beta"), new Release().setName("alpha")) > 0);
assertEquals(nameComparator.compare(new Release().setName("alpha"), new Release().setName("alpha")), 0);
}

@Test
public void checkSearchResultComparatorName() throws ResourceClassNotFoundException {
Comparator<SearchResult> nameComparator = resourceComparatorGenerator.generateComparator(SW360Constants.TYPE_SEARCHRESULT, "name");
assertNotNull(nameComparator);
assertTrue(nameComparator.compare(new SearchResult().setName("alpha"), new SearchResult().setName("beta")) < 0);
assertTrue(nameComparator.compare(new SearchResult().setName("beta"), new SearchResult().setName("alpha")) > 0);
assertEquals(nameComparator.compare(new SearchResult().setName("alpha"), new SearchResult().setName("alpha")), 0);
}

@Test
public void checkSearchResultComparatorType() throws ResourceClassNotFoundException {
Comparator<SearchResult> nameComparator = resourceComparatorGenerator.generateComparator(SW360Constants.TYPE_SEARCHRESULT, "type");
assertNotNull(nameComparator);
assertTrue(nameComparator.compare(new SearchResult().setType("project"), new SearchResult().setType("release")) < 0);
assertTrue(nameComparator.compare(new SearchResult().setType("release"), new SearchResult().setType("project")) > 0);
assertEquals(nameComparator.compare(new SearchResult().setType("project"), new SearchResult().setType("project")), 0);
}
}
1 change: 1 addition & 0 deletions rest/resource-server/src/docs/asciidoc/api-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,4 @@ include::vendors.adoc[]
include::licenses.adoc[]
include::vulnerabilities.adoc[]
include::health.adoc[]
include::search.adoc[]
20 changes: 20 additions & 0 deletions rest/resource-server/src/docs/asciidoc/projects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,26 @@ include::{snippets}/should_document_get_project_releases/http-response.adoc[]
===== Links
include::{snippets}/should_document_get_project_releases/links.adoc[]

[[resources-project-get-projects-releases]]
==== Listing releases of multiple projects

A `GET` request will get releases of multiple projects. Please pass an array of project ids as request body

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

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

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

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

===== Links
include::{snippets}/should_document_get_projects_releases/links.adoc[]


[[resources-project-get-project-releases-transitive]]
==== Listing releases (transitive)
Expand Down
35 changes: 35 additions & 0 deletions rest/resource-server/src/docs/asciidoc/search.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Copyright Siemens AG, 2020. 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
//

[[resources-search]]
=== Search

The search resource is used get the search result


[[resources-search-list]]
==== Listing search results

A `GET` request will list all the search results based on the search text and type

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

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

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

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

===== Links
include::{snippets}/should_document_get_searchresult/links.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.sw360.datahandler.thrift.projects.ProjectRelationship;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectState;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectType;
import org.eclipse.sw360.datahandler.thrift.search.SearchResult;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.vendors.Vendor;
import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability;
Expand Down Expand Up @@ -68,6 +69,7 @@ public Sw360Module() {
setMixInAnnotation(COTSDetails.class, Sw360Module.COTSDetailsMixin.class);
setMixInAnnotation(ClearingInformation.class, Sw360Module.ClearingInformationMixin.class);
setMixInAnnotation(Repository.class, Sw360Module.RepositoryMixin.class);
setMixInAnnotation(SearchResult.class, Sw360Module.SearchResultMixin.class);
}

@JsonInclude(JsonInclude.Include.NON_NULL)
Expand Down Expand Up @@ -795,5 +797,20 @@ public static abstract class COTSDetailsMixin extends COTSDetails {
})
public static abstract class RepositoryMixin extends Repository {
}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties({
"score",
"details",
"setScore",
"detailsSize",
"detailsIterator",
"setDetails",
"setId",
"setType",
"setName"
})
public static abstract class SearchResultMixin extends SearchResult {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.eclipse.sw360.datahandler.common.CommonUtils;
import org.eclipse.sw360.datahandler.resourcelists.*;
import org.eclipse.sw360.datahandler.thrift.attachments.Attachment;
import org.eclipse.sw360.datahandler.thrift.components.ClearingState;
import org.eclipse.sw360.datahandler.thrift.components.Component;
import org.eclipse.sw360.datahandler.thrift.components.Release;
import org.eclipse.sw360.datahandler.thrift.licenses.License;
Expand All @@ -29,6 +30,7 @@
import org.eclipse.sw360.rest.resourceserver.project.ProjectController;
import org.eclipse.sw360.datahandler.thrift.components.ComponentService;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectService;
import org.eclipse.sw360.datahandler.thrift.MainlineState;
import org.eclipse.sw360.datahandler.thrift.SW360Exception;
import org.eclipse.sw360.rest.resourceserver.project.Sw360ProjectService;
import org.eclipse.sw360.rest.resourceserver.release.ReleaseController;
Expand Down Expand Up @@ -437,6 +439,14 @@ public Release convertToEmbeddedRelease(Release release) {
return embeddedRelease;
}

public Release convertToEmbeddedReleaseWithDet(Release release) {
List<String> fields = List.of("id", "name", "version", "cpeid", "createdBy", "createdOn", "componentId",
"additionalData", "clearingState", "mainLicenseIds", "binaryDownloadurl", "sourceCodeDownloadurl",
"releaseDate", "externalIds", "languages", "operatingSystems", "softwarePlatforms", "vendor",
"mainlineState");
return convertToEmbeddedRelease(release, fields);
}

public Release convertToEmbeddedRelease(Release release, List<String> fields) {
Release embeddedRelease = this.convertToEmbeddedRelease(release);
if (fields != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent;
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType;
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage;
import org.eclipse.sw360.datahandler.thrift.components.ClearingState;
import org.eclipse.sw360.datahandler.thrift.components.Release;
import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfo;
Expand Down Expand Up @@ -66,13 +67,15 @@
import org.springframework.boot.json.GsonJsonParser;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MultiValueMap;
Expand All @@ -93,12 +96,19 @@
import java.net.URISyntaxException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.eclipse.sw360.datahandler.common.CommonUtils.wrapThriftOptionalReplacement;
import static org.eclipse.sw360.datahandler.common.WrappedException.wrapException;
import static org.eclipse.sw360.datahandler.common.WrappedException.wrapTException;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;

Expand Down Expand Up @@ -292,6 +302,46 @@ public ResponseEntity<Resources<Resource<Release>>> getProjectReleases(
return new ResponseEntity<>(resources, status);
}

@RequestMapping(value = PROJECTS_URL + "/releases", method = RequestMethod.GET)
public ResponseEntity<Resources<Resource<Release>>> getProjectsReleases(
Pageable pageable,
@RequestBody List<String> projectIds,
@RequestParam(value = "transitive", required = false) String transitive,@RequestParam(value = "clearingState", required = false) String clState, HttpServletRequest request) throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException {

final User sw360User = restControllerHelper.getSw360UserFromAuthentication();
final Set<String> releaseIdsInBranch = new HashSet<>();
boolean isTransitive = Boolean.parseBoolean(transitive);

Set<Release> releases = projectService.getReleasesFromProjectIds(projectIds, transitive, sw360User,releaseService);

if (null != clState) {
ClearingState cls = ThriftEnumUtils.stringToEnum(clState, ClearingState.class);
releases = releases.stream().filter(rel -> rel.getClearingState().equals(cls)).collect(Collectors.toSet());
}

List<Release> relList = releases.stream().collect(Collectors.toList());

PaginationResult<Release> paginationResult = restControllerHelper.createPaginationResult(request, pageable, relList, SW360Constants.TYPE_RELEASE);
final List<Resource<Release>> releaseResources = paginationResult.getResources().stream()
.map(sw360Release -> wrapTException(() -> {
final Release embeddedRelease = restControllerHelper.convertToEmbeddedReleaseWithDet(sw360Release);
final HalResource<Release> releaseResource = new HalResource<>(embeddedRelease);
if (isTransitive) {
projectService.addEmbeddedlinkedRelease(sw360Release, sw360User, releaseResource,
releaseService, releaseIdsInBranch);
}
return releaseResource;
})).collect(Collectors.toList());

Resources resources = null;
if (CommonUtils.isNotEmpty(releaseResources)) {
resources = restControllerHelper.generatePagesResource(paginationResult, releaseResources);
}

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

@RequestMapping(value = PROJECTS_URL + "/{id}/releases/ecc", method = RequestMethod.GET)
public ResponseEntity<Resources<Resource<Release>>> getECCsOfReleases(
@PathVariable("id") String id,
Expand Down Expand Up @@ -353,15 +403,15 @@ public ResponseEntity<Resources<Resource<VulnerabilityDTO>>> updateVulnerabiliti
Set<String> externalIdsFromRequestDto = vulnDTOs.stream().map(VulnerabilityDTO::getExternalId).collect(Collectors.toSet());
Set<String> commonExtIds = Sets.intersection(actualExternalId, externalIdsFromRequestDto);

if(CommonUtils.isNullOrEmptyCollection(commonExtIds)) {
if(CommonUtils.isNullOrEmptyCollection(commonExtIds) || commonExtIds.size() != externalIdsFromRequestDto.size()) {
throw new HttpMessageNotReadableException("External ID is not valid");
}

Set<String> actualReleaseIds = actualVDto.stream().map(VulnerabilityDTO::getIntReleaseId).collect(Collectors.toSet());
Set<String> releaseIdsFromRequestDto = vulnDTOs.stream().map(VulnerabilityDTO::getIntReleaseId).collect(Collectors.toSet());
Set<String> commonRelIds = Sets.intersection(actualReleaseIds, releaseIdsFromRequestDto);

if(CommonUtils.isNullOrEmptyCollection(commonRelIds)) {
if(CommonUtils.isNullOrEmptyCollection(commonRelIds) || commonRelIds.size() != releaseIdsFromRequestDto.size()) {
throw new HttpMessageNotReadableException("Release ID is not valid");
}

Expand Down
Loading

0 comments on commit 77730ce

Please sign in to comment.