Skip to content

Commit

Permalink
feat(rest) : Advanced Search for project page
Browse files Browse the repository at this point in the history
Signed-off-by: Keerthi B L <[email protected]>
  • Loading branch information
keerthi-bl committed Dec 24, 2024
1 parent d35afa4 commit 1537205
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 33 deletions.
19 changes: 19 additions & 0 deletions rest/resource-server/src/docs/asciidoc/projects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1233,3 +1233,22 @@ include::{snippets}/should_document_get_export_project_create_clearing_request/c

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

[[resources-projects-list-by-search]]
==== Filtering with more fields

A `GET` request to fetch filtered list of projects.

Note : send query parameter's value in encoded format. (Reference: `https://datatracker.ietf.org/doc/html/rfc3986`)

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

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

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

===== Links
include::{snippets}/should_document_get_projects_by_advance_search/links.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,15 @@
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage;
import org.eclipse.sw360.datahandler.thrift.attachments.CheckStatus;
import org.eclipse.sw360.datahandler.thrift.attachments.UsageData;
import org.eclipse.sw360.datahandler.thrift.components.ClearingState;
import org.eclipse.sw360.datahandler.thrift.components.Release;
import org.eclipse.sw360.datahandler.thrift.components.ReleaseClearingStateSummary;
import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink;
import org.eclipse.sw360.datahandler.thrift.components.ReleaseNode;
import org.eclipse.sw360.datahandler.thrift.components.*;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfo;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoFile;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoParsingResult;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseNameWithText;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.OutputFormatInfo;
import org.eclipse.sw360.datahandler.thrift.licenseinfo.OutputFormatVariant;
import org.eclipse.sw360.datahandler.thrift.licenses.License;
import org.eclipse.sw360.datahandler.thrift.projects.ObligationList;
import org.eclipse.sw360.datahandler.thrift.projects.ObligationStatusInfo;
import org.eclipse.sw360.datahandler.thrift.projects.Project;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectProjectRelationship;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectRelationship;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectDTO;
import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest;
import org.eclipse.sw360.datahandler.thrift.projects.*;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
import org.eclipse.sw360.datahandler.thrift.vendors.Vendor;
Expand Down Expand Up @@ -259,6 +247,16 @@ public ResponseEntity<CollectionModel<EntityModel<Project>>> getProjectsForUser(
@RequestParam(value = "tag", required = false) String tag,
@Parameter(description = "Flag to get projects with all details.")
@RequestParam(value = "allDetails", required = false) boolean allDetails,
@Parameter(description = "The version of the project")
@RequestParam(value = "version", required = false) String version,
@Parameter(description = "The projectResponsible of the project")
@RequestParam(value = "projectResponsible", required = false) String projectResponsible,
@Parameter(description = "The state of the project")
@RequestParam(value = "state", required = false) ProjectState projectState,
@Parameter(description = "The clearingStatus of the project")
@RequestParam(value = "clearingStatus", required = false) ProjectClearingState projectClearingState,
@Parameter(description = "The additionalData of the project")
@RequestParam(value = "additionalData", required = false) String additionalData,
@Parameter(description = "List project by lucene search")
@RequestParam(value = "luceneSearch", required = false) boolean luceneSearch,
HttpServletRequest request) throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException {
Expand All @@ -269,23 +267,13 @@ public ResponseEntity<CollectionModel<EntityModel<Project>>> getProjectsForUser(
boolean isSearchByType = CommonUtils.isNotNullEmptyOrWhitespace(projectType);
boolean isSearchByGroup = CommonUtils.isNotNullEmptyOrWhitespace(group);
boolean isNoFilter = false;
boolean isAllProjectAdded=false;
String queryString = request.getQueryString();
Map<String, String> params = restControllerHelper.parseQueryString(queryString);
List<Project> sw360Projects = new ArrayList<>();
Map<String, Set<String>> filterMap = new HashMap<>();
if (luceneSearch) {
if (CommonUtils.isNotNullEmptyOrWhitespace(projectType)) {
Set<String> values = CommonUtils.splitToSet(projectType);
filterMap.put(Project._Fields.PROJECT_TYPE.getFieldName(), values);
}
if (CommonUtils.isNotNullEmptyOrWhitespace(group)) {
Set<String> values = CommonUtils.splitToSet(group);
filterMap.put(Project._Fields.BUSINESS_UNIT.getFieldName(), values);
}
if (CommonUtils.isNotNullEmptyOrWhitespace(tag)) {
Set<String> values = CommonUtils.splitToSet(tag);
filterMap.put(Project._Fields.TAG.getFieldName(), values);
}
Map<String, Set<String>> filterMap = getFilterMap(tag, projectType, group, version, projectResponsible, projectState, projectClearingState,
additionalData);

if (CommonUtils.isNotNullEmptyOrWhitespace(name)) {
Set<String> values = CommonUtils.splitToSet(name);
Expand All @@ -298,21 +286,53 @@ public ResponseEntity<CollectionModel<EntityModel<Project>>> getProjectsForUser(
} else {
if (isSearchByName) {
sw360Projects.addAll(projectService.searchProjectByName(params.get("name"), sw360User));
} else if (isSearchByGroup) {
sw360Projects.addAll(projectService.searchProjectByGroup(group, sw360User));
} else if (isSearchByTag) {
sw360Projects.addAll(projectService.searchProjectByTag(params.get("tag"), sw360User));
} else if (isSearchByType) {
sw360Projects.addAll(projectService.searchProjectByType(projectType, sw360User));
} else {
isAllProjectAdded=true;
sw360Projects.addAll(projectService.getProjectsForUser(sw360User, pageable));
}
Map<String, Set<String>> restrictions = getFilterMap(tag, projectType, group, version, projectResponsible, projectState, projectClearingState,
additionalData);
if (!restrictions.isEmpty()) {
sw360Projects = new ArrayList<>(sw360Projects.stream()
.filter(filterProjectMap(restrictions)).toList());
}else if(isAllProjectAdded){
isNoFilter = true;
}
}
return getProjectResponse(pageable, projectType, group, tag, allDetails, luceneSearch, request, sw360User,
mapOfProjects, isSearchByName, sw360Projects, isNoFilter);
}

private Map<String, Set<String>> getFilterMap(String tag, String projectType, String group, String version, String projectResponsible,
ProjectState projectState, ProjectClearingState projectClearingState, String additionalData) {
Map<String, Set<String>> filterMap = new HashMap<>();
if (CommonUtils.isNotNullEmptyOrWhitespace(tag)) {
filterMap.put(Project._Fields.TAG.getFieldName(), CommonUtils.splitToSet(tag));
}
if (CommonUtils.isNotNullEmptyOrWhitespace(projectType)) {
filterMap.put(Project._Fields.PROJECT_TYPE.getFieldName(), CommonUtils.splitToSet(projectType));
}
if (CommonUtils.isNotNullEmptyOrWhitespace(group)) {
filterMap.put(Project._Fields.BUSINESS_UNIT.getFieldName(), CommonUtils.splitToSet(group));
}
if (CommonUtils.isNotNullEmptyOrWhitespace(version)) {
filterMap.put(Project._Fields.VERSION.getFieldName(), CommonUtils.splitToSet(version));
}
if (CommonUtils.isNotNullEmptyOrWhitespace(projectResponsible)) {
filterMap.put(Project._Fields.PROJECT_RESPONSIBLE.getFieldName(), CommonUtils.splitToSet(projectResponsible));
}
if (projectState!=null && CommonUtils.isNotNullEmptyOrWhitespace(projectState.name())) {
filterMap.put(Project._Fields.STATE.getFieldName(), CommonUtils.splitToSet(projectState.name()));
}
if (projectClearingState!=null && CommonUtils.isNotNullEmptyOrWhitespace(projectClearingState.name())) {
filterMap.put(Project._Fields.CLEARING_STATE.getFieldName(), CommonUtils.splitToSet(projectClearingState.name()));
}
if (CommonUtils.isNotNullEmptyOrWhitespace(additionalData)) {
filterMap.put(Project._Fields.ADDITIONAL_DATA.getFieldName(), CommonUtils.splitToSet(additionalData));
}
return filterMap;
}

@NotNull
private ResponseEntity<CollectionModel<EntityModel<Project>>> getProjectResponse(Pageable pageable,
String projectType, String group, String tag, boolean allDetails, boolean luceneSearch,
Expand Down Expand Up @@ -3396,4 +3416,48 @@ public ResponseEntity<?> createDuplicateProjectWithDependencyNetwork(

return ResponseEntity.created(location).body(projectDTOHalResource);
}

/**
* Create a filter predicate to remove all projects which do not satisfy the restriction set.
* @param restrictions Restrictions set to filter projects on
* @return Filter predicate for stream.
*/
private static @NonNull Predicate<Project> filterProjectMap(Map<String, Set<String>> restrictions) {
return project -> {
for (Map.Entry<String, Set<String>> restriction : restrictions.entrySet()) {
final Set<String> filterSet = restriction.getValue();
Project._Fields field = Project._Fields.findByName(restriction.getKey());
Object fieldValue = project.getFieldValue(field);
if (fieldValue == null) {
return false;
}
if (field == Project._Fields.PROJECT_TYPE && !filterSet.contains(project.projectType.name())) {
return false;
} else if (field == Project._Fields.VERSION && !filterSet.contains(project.version)) {
return false;
} else if (field == Project._Fields.PROJECT_RESPONSIBLE && !filterSet.contains(project.projectResponsible)) {
return false;
} else if (field == Project._Fields.STATE && !filterSet.contains(project.state.name())) {
return false;
} else if (field == Project._Fields.CLEARING_STATE && !filterSet.contains(project.clearingState.name())) {
return false;
} else if ((field == Project._Fields.CREATED_BY || field == Project._Fields.CREATED_ON)
&& !fieldValue.toString().equalsIgnoreCase(filterSet.iterator().next())) {
return false;
} else if (fieldValue instanceof Set) {
if (Sets.intersection(filterSet, (Set<String>) fieldValue).isEmpty()) {
return false;
}
} else if (fieldValue instanceof Map<?,?>) {
Map<?, ?> fieldValueMap = (Map<?, ?>) fieldValue;
boolean hasIntersection = fieldValueMap.keySet().stream()
.anyMatch(filterSet::contains);
if (!hasIntersection) {
return false;
}
}
}
return true;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3200,4 +3200,45 @@ public void should_document_get_project_release_with_ecc_spreadsheet() throws Ex
parameterWithName("projectId").description("Id of a project"))
));
}

@Test
public void should_document_get_projects_by_advance_search() throws Exception {
mockMvc.perform(get("/api/projects")
.header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword))
.queryParam("projectType", project.getProjectType().toString())
.queryParam("createdOn", project.getCreatedOn())
.queryParam("version", project.getVersion())
.queryParam("luceneSearch", "false")
.queryParam("page", "0")
.queryParam("page_entries", "5")
.queryParam("sort", "name,desc")
.accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
queryParameters(
parameterWithName("projectType").description("Filter for type"),
parameterWithName("createdOn").description("Filter for project creation date"),
parameterWithName("version").description("Filter for version"),
parameterWithName("luceneSearch").description("Filter with exact match or lucene match."),
parameterWithName("page").description("Page of projects"),
parameterWithName("page_entries").description("Amount of projects per page"),
parameterWithName("sort").description("Defines order of the projects")
),
links(
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:projects.[]name").description("The name of the component"),
subsectionWithPath("_embedded.sw360:projects.[]projectType").description("The component type, possible values are: " + Arrays.asList(ComponentType.values())),
subsectionWithPath("_embedded.sw360:projects").description("An array of <<resources-projects, Projects resources>>"),
subsectionWithPath("_links").description("<<resources-index-links,Links>> to other resources"),
fieldWithPath("page").description("Additional paging information"),
fieldWithPath("page.size").description("Number of projects per page"),
fieldWithPath("page.totalElements").description("Total number of all existing projects"),
fieldWithPath("page.totalPages").description("Total number of pages"),
fieldWithPath("page.number").description("Number of the current page")
)));
}
}

0 comments on commit 1537205

Please sign in to comment.