Skip to content

Commit

Permalink
Merge pull request #2028 from siemens/fix/linkedProjectsTransitive
Browse files Browse the repository at this point in the history
feat(rest): To list linked projects of sub-projects.

Reviewed by : [email protected]
Tested by: [email protected]
  • Loading branch information
ag4ums authored Jul 17, 2023
2 parents 6f21ffb + 447143b commit 3822b90
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 37 deletions.
22 changes: 21 additions & 1 deletion rest/resource-server/src/docs/asciidoc/projects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ include::{snippets}/should_document_get_project/links.adoc[]
[[resources-project-get-linked-projects]]
==== Listing linked projects

A `GET` request will get linked projects of a single project.
A `GET` request will get linked projects of a single project. +
Only linked projects without any projects of sub-projects `&transitive=false`.

===== Response structure
include::{snippets}/should_document_get_linked_projects/response-fields.adoc[]
Expand All @@ -322,6 +323,25 @@ include::{snippets}/should_document_get_linked_projects/http-response.adoc[]
include::{snippets}/should_document_get_linked_projects/links.adoc[]


[[resources-project-get-linked-projects]]
==== Listing linked projects (transitive)

A `GET` request will get linked projects of a single project. +
Please set the request parameter `&transitive=true`.

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

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

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

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


[[resources-project-get-project-releases]]
==== Listing releases

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,20 @@ public Project convertToEmbeddedProject(Project project) {
return embeddedProject;
}

public Project convertToEmbeddedLinkedProject(Project project) {
Project embeddedProject = new EmbeddedProject();
embeddedProject.setName(project.getName());
embeddedProject.setId(project.getId());
embeddedProject.setProjectType(project.getProjectType());
embeddedProject.setState(project.getState());
embeddedProject.setClearingState(project.getClearingState());
embeddedProject.setVersion(project.getVersion());
embeddedProject.setReleaseIdToUsage(project.getReleaseIdToUsage());
embeddedProject.setLinkedProjects(project.getLinkedProjects());
embeddedProject.setType(null);
return embeddedProject;
}

public void addEmbeddedComponent(HalResource halResource, Component component) {
Component embeddedComponent = convertToEmbeddedComponent(component);
HalResource<Component> halComponent = new HalResource<>(embeddedComponent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,41 +340,48 @@ public ResponseEntity<EntityModel<Project>> getProject(
}

@RequestMapping(value = PROJECTS_URL + "/{id}/linkedProjects", method = RequestMethod.GET)
public ResponseEntity<CollectionModel<EntityModel>> getLinkedProject(
Pageable pageable,
@PathVariable("id") String id,
HttpServletRequest request) throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException {

User sw360User = restControllerHelper.getSw360UserFromAuthentication();
Project sw360Proj = projectService.getProjectForUserById(id, sw360User);

Map<String, ProjectProjectRelationship> linkedProjects = sw360Proj.getLinkedProjects();
List<String> keys = new ArrayList<>(linkedProjects.keySet());
List<Project> projects = keys.stream().map(projId -> wrapTException(() -> {
final Project sw360Project = projectService.getProjectForUserById(projId, sw360User);
return sw360Project;
})).collect(Collectors.toList());

PaginationResult<Project> paginationResult = restControllerHelper.createPaginationResult(request, pageable,
projects, SW360Constants.TYPE_PROJECT);

final List<EntityModel<Project>> projectResources = paginationResult.getResources().stream()
.map(sw360Project -> wrapTException(() -> {
final Project embeddedProject = restControllerHelper.convertToEmbeddedProject(sw360Project);
final HalResource<Project> projectResource = new HalResource<>(embeddedProject);
return projectResource;
})).collect(Collectors.toList());

CollectionModel resources;
if (projectResources.size() == 0) {
resources = restControllerHelper.emptyPageResource(Project.class, paginationResult);
} else {
resources = restControllerHelper.generatePagesResource(paginationResult, projectResources);
}

HttpStatus status = resources == null ? HttpStatus.NO_CONTENT : HttpStatus.OK;
return new ResponseEntity<>(resources, status);
}
public ResponseEntity<CollectionModel<EntityModel>> getLinkedProject(Pageable pageable,
@PathVariable("id") String id,@RequestParam(value = "transitive", required = false) String transitive, HttpServletRequest request)
throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException {

User sw360User = restControllerHelper.getSw360UserFromAuthentication();
Project sw360Proj = projectService.getProjectForUserById(id, sw360User);
final Set<String> projectIdsInBranch = new HashSet<>();
boolean isTransitive = Boolean.parseBoolean(transitive);

Map<String, ProjectProjectRelationship> linkedProjects = sw360Proj.getLinkedProjects();
List<String> keys = new ArrayList<>(linkedProjects.keySet());
List<Project> projects = keys.stream().map(projId -> wrapTException(() -> {
final Project sw360Project = projectService.getProjectForUserById(projId, sw360User);
return sw360Project;
})).collect(Collectors.toList());

PaginationResult<Project> paginationResult = restControllerHelper.createPaginationResult(request, pageable,
projects, SW360Constants.TYPE_PROJECT);

final List<EntityModel<Project>> projectResources = paginationResult.getResources().stream()
.map(sw360Project -> wrapTException(() -> {
final Project embeddedProject = restControllerHelper.convertToEmbeddedLinkedProject(sw360Project);
final HalResource<Project> projectResource = new HalResource<>(embeddedProject);
System.out.println("before " + isTransitive);
if (isTransitive) {
System.out.println("after " + isTransitive);
projectService.addEmbeddedLinkedProject(sw360Project, sw360User, projectResource,
projectIdsInBranch);
}
return projectResource;
})).collect(Collectors.toList());

CollectionModel resources;
if (projectResources.size() == 0) {
resources = restControllerHelper.emptyPageResource(Project.class, paginationResult);
} else {
resources = restControllerHelper.generatePagesResource(paginationResult, projectResources);
}

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

@RequestMapping(value = PROJECTS_URL + "/{id}", method = RequestMethod.DELETE)
public ResponseEntity deleteProject(@PathVariable("id") String id) throws TException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.eclipse.sw360.datahandler.thrift.projects.ProjectData;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectService;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectProjectRelationship;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.rest.resourceserver.Sw360ResourceServer;
import org.eclipse.sw360.rest.resourceserver.core.AwareOfRestServices;
Expand Down Expand Up @@ -259,6 +260,31 @@ public Set<String> getReleaseIds(String projectId, User sw360User, String transi
}
}

public void addEmbeddedLinkedProject(Project sw360Project, User sw360User, HalResource<Project> projectResource, Set<String> projectIdsInBranch) throws TException {
projectIdsInBranch.add(sw360Project.getId());
Map<String, ProjectProjectRelationship> linkedProjects = sw360Project.getLinkedProjects();
List<String> keys = new ArrayList<>(linkedProjects.keySet());
System.out.println("keys " + keys.size());
if (keys != null) {
keys.forEach(linkedProjectId -> wrapTException(() -> {
if (projectIdsInBranch.contains(linkedProjectId)) {
return;
}
Project linkedProject = getProjectForUserById(linkedProjectId, sw360User);
System.out.println("project " + linkedProject);
Project embeddedLinkedProject = rch.convertToEmbeddedLinkedProject(linkedProject);
HalResource<Project> halLinkedProject = new HalResource<>(embeddedLinkedProject);
Link projectLink = linkTo(ProjectController.class)
.slash("api/projects/" + embeddedLinkedProject.getId()).withSelfRel();
halLinkedProject.add(projectLink);
addEmbeddedLinkedProject(linkedProject, sw360User, halLinkedProject,
projectIdsInBranch);
projectResource.addEmbeddedResource("sw360:linkedProjects", halLinkedProject);
}));
}
projectIdsInBranch.remove(sw360Project.getId());
}

public void addEmbeddedlinkedRelease(Release sw360Release, User sw360User, HalResource<Release> releaseResource,
Sw360ReleaseService releaseService, Set<String> releaseIdsInBranch) throws TException {
releaseIdsInBranch.add(sw360Release.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,50 @@ public void before() throws TException, IOException {

projectList.add(project2);

Map<String, ProjectReleaseRelationship> linkedReleases2 = new HashMap<>();
Map<String, ProjectProjectRelationship> linkedProjects2 = new HashMap<>();
Map<String, ProjectProjectRelationship> linkedProjects3 = new HashMap<>();
Project project4 = new Project();
project4.setId("12345");
project4.setName("dummy");
project4.setVersion("2.0.1");
project4.setProjectType(ProjectType.PRODUCT);
project4.setState(ProjectState.ACTIVE);
project4.setClearingState(ProjectClearingState.OPEN);
linkedProjects2.put("123456", new ProjectProjectRelationship(ProjectRelationship.CONTAINED).setEnableSvm(true));
project4.setLinkedProjects(linkedProjects2);
project4.setSecurityResponsibles(new HashSet<>(Arrays.asList("[email protected]", "[email protected]")));

Project project5 = new Project();
project5.setId("123456");
project5.setName("dummy2");
project5.setVersion("2.0.1");
project5.setProjectType(ProjectType.PRODUCT);
project5.setState(ProjectState.ACTIVE);
project5.setClearingState(ProjectClearingState.OPEN);
linkedReleases2.put("37652765121", projectReleaseRelationship);
project5.setReleaseIdToUsage(linkedReleases2);
linkedProjects3.put("1234567", new ProjectProjectRelationship(ProjectRelationship.CONTAINED).setEnableSvm(true));
project5.setLinkedProjects(linkedProjects3);
project5.setSecurityResponsibles(new HashSet<>(Arrays.asList("[email protected]", "[email protected]")));

Project project6 = new Project();
project6.setId("1234567");
project6.setName("dummy3");
project6.setVersion("3.0.1");
project6.setProjectType(ProjectType.PRODUCT);
project6.setState(ProjectState.ACTIVE);
project6.setClearingState(ProjectClearingState.OPEN);
project6.setSecurityResponsibles(new HashSet<>(Arrays.asList("[email protected]", "[email protected]")));

Release release5 = new Release();
release5.setId("37652765121");
release5.setName("Angular 2.3.1");
release5.setCpeid("cpe:/a:Google:Angular:2.3.1:");
release5.setReleaseDate("2016-12-17");
release5.setVersion("2.3.1");
release5.setCreatedOn("2016-12-28");

Set<String> releaseIds = new HashSet<>(Collections.singletonList("3765276512"));
Set<String> releaseIdsTransitive = new HashSet<>(Arrays.asList("3765276512", "5578999"));

Expand Down Expand Up @@ -348,6 +392,9 @@ public void before() throws TException, IOException {
given(this.projectServiceMock.getProjectsForUser(any(), any())).willReturn(projectList);
given(this.projectServiceMock.getProjectForUserById(eq(project.getId()), any())).willReturn(project);
given(this.projectServiceMock.getProjectForUserById(eq(project2.getId()), any())).willReturn(project2);
given(this.projectServiceMock.getProjectForUserById(eq(project4.getId()), any())).willReturn(project4);
given(this.projectServiceMock.getProjectForUserById(eq(project5.getId()), any())).willReturn(project5);
given(this.projectServiceMock.getProjectForUserById(eq(project6.getId()), any())).willReturn(project6);
given(this.projectServiceMock.getProjectForUserById(eq(projectForAtt.getId()), any())).willReturn(projectForAtt);
given(this.projectServiceMock.getProjectForUserById(eq(SPDXProject.getId()), any())).willReturn(SPDXProject);
given(this.projectServiceMock.getProjectForUserById(eq(cycloneDXProject.getId()), any())).willReturn(cycloneDXProject);
Expand Down Expand Up @@ -1016,6 +1063,7 @@ public void should_document_get_linked_projects() throws Exception {
String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword);
mockMvc.perform(get("/api/projects/" + project.getId() + "/linkedProjects")
.header("Authorization", "Bearer " + accessToken)
.param("transitive", "false")
.param("page", "0")
.param("page_entries", "5")
.param("sort", "name,desc")
Expand All @@ -1025,7 +1073,41 @@ public void should_document_get_linked_projects() throws Exception {
requestParameters(
parameterWithName("page").description("Page of projects"),
parameterWithName("page_entries").description("Amount of projects page"),
parameterWithName("sort").description("Defines order of the projects")
parameterWithName("sort").description("Defines order of the projects"),
parameterWithName("transitive").description("Get the transitive 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").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")
)));
}

@Test
public void should_document_get_linked_projects_transitive() throws Exception {
String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword);
mockMvc.perform(get("/api/projects/" + "12345" + "/linkedProjects?transitive=true")
.header("Authorization", "Bearer " + accessToken)
.param("page", "0")
.param("page_entries", "5")
.param("sort", "name,desc")
.accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
requestParameters(
parameterWithName("page").description("Page of projects"),
parameterWithName("page_entries").description("Amount of projects page"),
parameterWithName("sort").description("Defines order of the projects"),
parameterWithName("transitive").description("Get the transitive projects")
),
links(
linkWithRel("curies").description("Curies are used for online documentation"),
Expand Down

0 comments on commit 3822b90

Please sign in to comment.