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 eb9a778
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 24 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,11 +69,7 @@
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;
Expand Down Expand Up @@ -259,6 +255,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) String ProjectState,
@Parameter(description = "The clearingStatus of the project")
@RequestParam(value = "clearingStatus", required = false) String 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 +275,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 +294,52 @@ 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, String ProjectState, String 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 (CommonUtils.isNotNullEmptyOrWhitespace(ProjectState)) {
filterMap.put(Project._Fields.STATE.getFieldName(), CommonUtils.splitToSet(ProjectState));
}
if (CommonUtils.isNotNullEmptyOrWhitespace(ProjectClearingState)) {
filterMap.put(Project._Fields.CLEARING_STATE.getFieldName(), CommonUtils.splitToSet(ProjectClearingState));
}
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 +3423,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 eb9a778

Please sign in to comment.