diff --git a/rest/resource-server/src/docs/asciidoc/projects.adoc b/rest/resource-server/src/docs/asciidoc/projects.adoc index a3efb045e8..8b46b65263 100644 --- a/rest/resource-server/src/docs/asciidoc/projects.adoc +++ b/rest/resource-server/src/docs/asciidoc/projects.adoc @@ -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[] @@ -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 diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java index 4ab9fd72c8..6eb3336154 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java @@ -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 halComponent = new HalResource<>(embeddedComponent); diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java index c5463cdd30..29e3cce596 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java @@ -340,41 +340,48 @@ public ResponseEntity> getProject( } @RequestMapping(value = PROJECTS_URL + "/{id}/linkedProjects", method = RequestMethod.GET) - public ResponseEntity> 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 linkedProjects = sw360Proj.getLinkedProjects(); - List keys = new ArrayList<>(linkedProjects.keySet()); - List projects = keys.stream().map(projId -> wrapTException(() -> { - final Project sw360Project = projectService.getProjectForUserById(projId, sw360User); - return sw360Project; - })).collect(Collectors.toList()); - - PaginationResult paginationResult = restControllerHelper.createPaginationResult(request, pageable, - projects, SW360Constants.TYPE_PROJECT); - - final List> projectResources = paginationResult.getResources().stream() - .map(sw360Project -> wrapTException(() -> { - final Project embeddedProject = restControllerHelper.convertToEmbeddedProject(sw360Project); - final HalResource 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> 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 projectIdsInBranch = new HashSet<>(); + boolean isTransitive = Boolean.parseBoolean(transitive); + + Map linkedProjects = sw360Proj.getLinkedProjects(); + List keys = new ArrayList<>(linkedProjects.keySet()); + List projects = keys.stream().map(projId -> wrapTException(() -> { + final Project sw360Project = projectService.getProjectForUserById(projId, sw360User); + return sw360Project; + })).collect(Collectors.toList()); + + PaginationResult paginationResult = restControllerHelper.createPaginationResult(request, pageable, + projects, SW360Constants.TYPE_PROJECT); + + final List> projectResources = paginationResult.getResources().stream() + .map(sw360Project -> wrapTException(() -> { + final Project embeddedProject = restControllerHelper.convertToEmbeddedLinkedProject(sw360Project); + final HalResource 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 { diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/Sw360ProjectService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/Sw360ProjectService.java index f1a551a4a8..218ea6c178 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/Sw360ProjectService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/Sw360ProjectService.java @@ -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; @@ -259,6 +260,31 @@ public Set getReleaseIds(String projectId, User sw360User, String transi } } + public void addEmbeddedLinkedProject(Project sw360Project, User sw360User, HalResource projectResource, Set projectIdsInBranch) throws TException { + projectIdsInBranch.add(sw360Project.getId()); + Map linkedProjects = sw360Project.getLinkedProjects(); + List 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 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 releaseResource, Sw360ReleaseService releaseService, Set releaseIdsInBranch) throws TException { releaseIdsInBranch.add(sw360Release.getId()); diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java index c5247e70b4..d78249e4b3 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java @@ -296,6 +296,50 @@ public void before() throws TException, IOException { projectList.add(project2); + Map linkedReleases2 = new HashMap<>(); + Map linkedProjects2 = new HashMap<>(); + Map 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("securityresponsible1@sw360.org", "securityresponsible2@sw360.org"))); + + 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("securityresponsible1@sw360.org", "securityresponsible2@sw360.org"))); + + 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("securityresponsible1@sw360.org", "securityresponsible2@sw360.org"))); + + 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 releaseIds = new HashSet<>(Collections.singletonList("3765276512")); Set releaseIdsTransitive = new HashSet<>(Arrays.asList("3765276512", "5578999")); @@ -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); @@ -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") @@ -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 <>"), + subsectionWithPath("_links").description("<> 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"),