diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java index 852d1bdc65..81b6373724 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java @@ -633,8 +633,37 @@ private void saveAttachmentUsages(ResourceRequest request, ResourceResponse resp User user = UserCacheHolder.getUserFromRequest(request); if (PermissionUtils.makePermission(project, user).isActionAllowed(RequestedAction.WRITE)) { List deselectedUsagesFromRequest = ProjectPortletUtils.deselectedAttachmentUsagesFromRequest(request); + System.out.println("list of deselectedUsagesFromRequest :" + deselectedUsagesFromRequest); List selectedUsagesFromRequest = ProjectPortletUtils.selectedAttachmentUsagesFromRequest(request); + System.out.println("list of selectedUsagesFromRequest :" + selectedUsagesFromRequest); List allUsagesByProject = attachmentClient.getUsedAttachments(Source.projectId(projectId), null); + System.out.println("list of allUsagesByProject :" + allUsagesByProject); + List selectedData = new ArrayList<>(); + for (AttachmentUsage usage : allUsagesByProject) { + if (usage.getUsageData().getSetField().equals(UsageData._Fields.LICENSE_INFO)) { + StringBuilder result = new StringBuilder(); + result.append(usage.getUsageData().getLicenseInfo().getProjectPath()) + .append("-") + .append(usage.getOwner().getReleaseId()) + .append("_") + .append(usage.getUsageData().getSetField().getFieldName()) + .append("_") + .append(usage.getAttachmentContentId()); + String stringResult = result.toString(); + selectedData.add(stringResult); + } + else { + StringBuilder result = new StringBuilder(); + result.append(usage.getOwner().getReleaseId()) + .append("_") + .append(usage.getUsageData().getSetField().getFieldName()) + .append("_") + .append(usage.getAttachmentContentId()); + String stringResult = result.toString(); + selectedData.add(stringResult); + } + } + System.out.println("Resulting string: " + selectedData); List usagesToDelete = allUsagesByProject.stream() .filter(usage -> deselectedUsagesFromRequest.stream() .anyMatch(isUsageEquivalent(usage))) diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortletUtils.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortletUtils.java index c0bd25efac..3b4f69a296 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortletUtils.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortletUtils.java @@ -407,11 +407,15 @@ private static List makeAttachmentUsagesFromRequestParameters(R BiFunction, Set, Set> computeUsagesFromCheckboxes, boolean deselectUsage) { final String projectId = request.getParameter(PROJECT_ID); Set selectedUsages = new HashSet<>(arrayToList(request.getParameterValues(PROJECT_SELECTED_ATTACHMENT_USAGES))); + System.out.println("Set of selectedUsages: " + selectedUsages); Set changedUsages = new HashSet<>(arrayToList(request.getParameterValues(PROJECT_SELECTED_ATTACHMENT_USAGES_SHADOWS))); + System.out.println("Set of changedUsages: " + changedUsages); Set changedIncludeConludedLicenses = new HashSet<>( arrayToList(request.getParameterValues(INCLUDE_CONCLUDED_LICENSE_SHADOWS))); + System.out.println("changedIncludeConludedLicenses = " + changedIncludeConludedLicenses); changedUsages = Sets.union(changedUsages, new HashSet(changedIncludeConludedLicenses)); List includeConludedLicenses = arrayToList(request.getParameterValues(INCLUDE_CONCLUDED_LICENSE)); + System.out.println("includeConludedLicenses = " + includeConludedLicenses); Set usagesSubset = computeUsagesFromCheckboxes.apply(changedUsages, selectedUsages); if (deselectUsage) { usagesSubset = Sets.union(usagesSubset, new HashSet(changedIncludeConludedLicenses)); diff --git a/rest/resource-server/src/docs/asciidoc/projects.adoc b/rest/resource-server/src/docs/asciidoc/projects.adoc index b5baf4b0d3..f834589595 100644 --- a/rest/resource-server/src/docs/asciidoc/projects.adoc +++ b/rest/resource-server/src/docs/asciidoc/projects.adoc @@ -650,6 +650,30 @@ include::{snippets}/should_document_get_attachment_usage_for_project/curl-reques ===== Example response include::{snippets}/should_document_get_attachment_usage_for_project/http-response.adoc[] +[[resources-project-save-attachmentUsages]] +==== Save attachmentUsages of the project + +A `POST` request is used to save attachmentUsages of the project. Please pass a Map> having string as key and list as value in request body. + +===== Request structure 1 +Pass a Map> in request body. + +Keys can be selected, deselected, selectedConcludedUsages or deselectedConcludedUsages. + +Format of String inside list varies according to the usageFields (sourcePackage, licenseInfo, manuallySet) : + +for `sourcePackage`, releaseId_sourcePakage_attachmentContentId + +for `licenseInfo`, projectId-releaseId_licenseInfo_attachmentContentId (for directly linked project) OR projectPath-releaseId_licenseInfo_attachmentContentId (for nested projects) + +for `manuallySet`, releaseId_manuallySet_attachmentContentId + +===== Example request 1 +include::{snippets}/should_document_save_usages/curl-request.adoc[] + +===== Example response 1 +include::{snippets}/should_document_save_usages/http-response.adoc[] + [[resources-project-get-project-vulnerabilities]] ==== Listing project vulnerabilities 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 e18edcf1d9..7a791ea5b4 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 @@ -57,6 +57,7 @@ import org.eclipse.sw360.datahandler.thrift.Source; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; +import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentService; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage; import org.eclipse.sw360.datahandler.thrift.attachments.UsageData; @@ -1492,6 +1493,87 @@ public ResponseEntity>> getUsedByProjectDet return new ResponseEntity<>(resources, status); } + @PreAuthorize("hasAuthority('WRITE')") + @Operation( + summary = "save attachment usages", + description = "Pass an array of string in request body.", + tags = {"Projects"} + ) + @RequestMapping(value = PROJECTS_URL + "/{id}/saveAttachmentUsages", method = RequestMethod.POST) + public ResponseEntity saveAttachmentUsages( + @Parameter(description = "Project ID.") + @PathVariable("id") String id, + @Parameter(description = "Map of key-value pairs where each key is associated with a list of strings.", + example = "{\"selected\": [\"4427a8e723ad405db63f75170ef240a2_sourcePackage_5c5d6f54ac6a4b33bcd3c5d3a8fefc43\", \"value2\"]," + + " \"deselected\": [\"de213309ba0842ac8a7251bf27ea8f36_manuallySet_eec66c3465f64f0292dfc2564215c681\", \"value2\"]}," + + " \"selectedConcludedUsages\": [\"de213309ba0842ac8a7251bf27ea8f36_licenseInfo_eec66c3465f64f0292dfc2564215c681\", \"value2\"]}," + + " \"deselectedConcludedUsages\": [\"ade213309ba0842ac8a7251bf27ea8f36_licenseInfo_aeec66c3465f64f0292dfc2564215c681\", \"value2\"]}" + ) + @RequestBody Map> allUsages + ) throws TException { + final User user = restControllerHelper.getSw360UserFromAuthentication(); + final Project project = projectService.getProjectForUserById(id, user); + try { + if (PermissionUtils.makePermission(project, user).isActionAllowed(RequestedAction.WRITE)) { + Source usedBy = Source.projectId(id); + List selectedUsages = new ArrayList<>(); + List deselectedUsages = new ArrayList<>(); + List selectedConcludedUsages = new ArrayList<>(); + List deselectedConcludedUsages = new ArrayList<>(); + List changedUsages = new ArrayList<>(); + for (Map.Entry> entry : allUsages.entrySet()) { + String key = entry.getKey(); + List list = entry.getValue(); + if (key.equals("selected")) { + selectedUsages.addAll(list); + } else if (key.equals("deselected")) { + deselectedUsages.addAll(list); + } else if (key.equals("selectedConcludedUsages")) { + selectedConcludedUsages.addAll(list); + } else if (key.equals("deselectedConcludedUsages")) { + deselectedConcludedUsages.addAll(list); + } + } + Set totalReleaseIds = projectService.getReleaseIds(id, user, true); + changedUsages.addAll(selectedUsages); + changedUsages.addAll(deselectedUsages); + boolean valid = projectService.validate(changedUsages, user, releaseService, totalReleaseIds); + if (!valid) { + return new ResponseEntity<>("Not a valid attachment type OR release does not belong to project", HttpStatus.CONFLICT); + } + List allUsagesByProject = projectService.getUsedAttachments(usedBy, null); + List savedUsages = projectService.savedUsages(allUsagesByProject); + savedUsages.removeAll(deselectedUsages); + deselectedUsages.addAll(selectedUsages); + selectedUsages.addAll(savedUsages); + deselectedConcludedUsages.addAll(selectedConcludedUsages); + List deselectedUsagesFromRequest = projectService.deselectedAttachmentUsagesFromRequest(deselectedUsages, selectedUsages, deselectedConcludedUsages, selectedConcludedUsages, id); + List selectedUsagesFromRequest = projectService.selectedAttachmentUsagesFromRequest(deselectedUsages, selectedUsages, deselectedConcludedUsages, selectedConcludedUsages, id); + List usagesToDelete = allUsagesByProject.stream() + .filter(usage -> deselectedUsagesFromRequest.stream() + .anyMatch(projectService.isUsageEquivalent(usage))) + .collect(Collectors.toList()); + if (!usagesToDelete.isEmpty()) { + projectService.deleteAttachmentUsages(usagesToDelete); + } + List allUsagesByProjectAfterCleanUp = projectService.getUsedAttachments(usedBy, null); + List usagesToCreate = selectedUsagesFromRequest.stream() + .filter(usage -> allUsagesByProjectAfterCleanUp.stream() + .noneMatch(projectService.isUsageEquivalent(usage))) + .collect(Collectors.toList()); + + if (!usagesToCreate.isEmpty()) { + projectService.makeAttachmentUsages(usagesToCreate); + } + return new ResponseEntity<>("AttachmentUsages Saved Successfully", HttpStatus.CREATED); + } else { + return new ResponseEntity<>("No write permission for project", HttpStatus.FORBIDDEN); + } + } catch (TException e) { + return new ResponseEntity<>("Saving attachment usages for project " + id + " failed", HttpStatus.INTERNAL_SERVER_ERROR); + } + } + @Operation( description = "Get all attachmentUsages of the projects.", tags = {"Projects"} 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 7f1349ae7e..96b99fc49f 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 @@ -28,9 +28,16 @@ import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.RequestSummary; import org.eclipse.sw360.datahandler.thrift.SW360Exception; +import org.eclipse.sw360.datahandler.thrift.Source; import org.eclipse.sw360.datahandler.thrift.ThriftClients; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; +import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentService; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType; +import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage; +import org.eclipse.sw360.datahandler.thrift.attachments.LicenseInfoUsage; +import org.eclipse.sw360.datahandler.thrift.attachments.ManuallySetUsage; +import org.eclipse.sw360.datahandler.thrift.attachments.SourcePackageUsage; +import org.eclipse.sw360.datahandler.thrift.attachments.UsageData; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.components.ReleaseClearingStatusData; import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink; @@ -47,6 +54,7 @@ import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper; import org.eclipse.sw360.rest.resourceserver.release.ReleaseController; import org.eclipse.sw360.rest.resourceserver.release.Sw360ReleaseService; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataIntegrityViolationException; @@ -57,7 +65,10 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; +import com.google.common.collect.Sets; + import javax.annotation.PreDestroy; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -65,6 +76,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; @@ -74,11 +86,13 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import static com.google.common.base.Strings.nullToEmpty; +import static org.eclipse.sw360.datahandler.common.CommonUtils.arrayToList; import static org.eclipse.sw360.datahandler.common.CommonUtils.getSortedMap; import static org.eclipse.sw360.datahandler.common.CommonUtils.isNullEmptyOrWhitespace; import static org.eclipse.sw360.datahandler.common.CommonUtils.nullToEmptyList; @@ -128,6 +142,179 @@ public Project getProjectForUserById(String projectId, User sw360User) throws TE } } + public boolean validate(List changedUsages, User sw360User, Sw360ReleaseService releaseService, Set totalReleaseIds) throws TException { + for (String data : changedUsages) { + String releaseId; + String usageData; + String attachmentContentId; + String[] parts = data.split("-"); + if (parts.length > 1) { + String[] components = parts[1].split("_"); + releaseId = components[0]; + usageData = components[1]; + attachmentContentId = components[2]; + } + else { + String[] components = data.split("_"); + releaseId = components[0]; + usageData = components[1]; + attachmentContentId = components[2]; + } + boolean relPresent = totalReleaseIds.contains(releaseId); + if (!relPresent) { + return false; + } + Release release = releaseService.getReleaseForUserById(releaseId, sw360User); + Set attachments = release.getAttachments(); + if (usageData.equals("sourcePackage")) { + for (Attachment attach : attachments) { + if (attach.getAttachmentContentId().equals(attachmentContentId) && (attach.getAttachmentType() != AttachmentType.SOURCE && attach.getAttachmentType() != AttachmentType.SOURCE_SELF)) { + return false; + } + } + } + if (usageData.equals("licenseInfo")) { + for (Attachment attach : attachments) { + if (attach.getAttachmentContentId().equals(attachmentContentId) && (attach.getAttachmentType() != AttachmentType.COMPONENT_LICENSE_INFO_COMBINED && attach.getAttachmentType() != AttachmentType.COMPONENT_LICENSE_INFO_XML)) { + return false; + } + } + } + } return true; + } + + public List savedUsages(List allUsagesByProject) { + List selectedData = new ArrayList<>(); + for (AttachmentUsage usage : allUsagesByProject) { + if (usage.getUsageData().getSetField().equals(UsageData._Fields.LICENSE_INFO)) { + StringBuilder result = new StringBuilder(); + result.append(usage.getUsageData().getLicenseInfo().getProjectPath()) + .append("-") + .append(usage.getOwner().getReleaseId()) + .append("_") + .append(usage.getUsageData().getSetField().getFieldName()) + .append("_") + .append(usage.getAttachmentContentId()); + String stringResult = result.toString(); + selectedData.add(stringResult); + } + else { + StringBuilder result = new StringBuilder(); + result.append(usage.getOwner().getReleaseId()) + .append("_") + .append(usage.getUsageData().getSetField().getFieldName()) + .append("_") + .append(usage.getAttachmentContentId()); + String stringResult = result.toString(); + selectedData.add(stringResult); + } + } + System.out.println("Resulting string: " + selectedData); + return selectedData; + } + + public List deselectedAttachmentUsagesFromRequest(List deselectedUsages, List selectedUsages, List deselectedConcludedUsages, List selectedConcludedUsages, String id) { + return makeAttachmentUsagesFromParameters(deselectedUsages, selectedUsages, deselectedConcludedUsages, selectedConcludedUsages, Sets::difference, true, id); + } + + public List selectedAttachmentUsagesFromRequest(List deselectedUsages, List selectedUsages, List deselectedConcludedUsages, List selectedConcludedUsages, String id) { + return makeAttachmentUsagesFromParameters(deselectedUsages, selectedUsages, deselectedConcludedUsages, selectedConcludedUsages, Sets::intersection, false, id); + } + + private static List makeAttachmentUsagesFromParameters(List deselectedUsages, List selectedUsage, + List deselectedConcludedUsages, List selectedConcludedUsages, + BiFunction, Set, Set> computeUsagesFromCheckboxes, boolean deselectUsage, String projectId) { + Set selectedUsages = new HashSet<>(selectedUsage); + System.out.println("Set of selectedUsages: " + selectedUsages); + Set changedUsages = new HashSet<>(deselectedUsages); + System.out.println("Set of changedUsages: " + changedUsages); + Set changedIncludeConludedLicenses = new HashSet<>(deselectedConcludedUsages); + changedUsages = Sets.union(changedUsages, new HashSet(changedIncludeConludedLicenses)); + List includeConludedLicenses = new ArrayList<>(selectedConcludedUsages); + Set usagesSubset = computeUsagesFromCheckboxes.apply(changedUsages, selectedUsages); + if (deselectUsage) { + usagesSubset = Sets.union(usagesSubset, new HashSet(changedIncludeConludedLicenses)); + } + return usagesSubset.stream() + .map(s -> parseAttachmentUsageFromString(projectId, s, includeConludedLicenses)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static AttachmentUsage parseAttachmentUsageFromString(String projectId, String s, List includeConludedLicense) { + String[] split = s.split("_"); + if (split.length != 3) { + log.warn(String.format("cannot parse attachment usage from %s for project id %s", s, projectId)); + return null; + } + + String releaseId = split[0]; + String type = split[1]; + String attachmentContentId = split[2]; + String projectPath = null; + if (UsageData._Fields.findByName(type).equals(UsageData._Fields.LICENSE_INFO)) { + String[] projectPath_releaseId = split[0].split("-"); + if (projectPath_releaseId.length == 2) { + releaseId = projectPath_releaseId[1]; + projectPath = projectPath_releaseId[0]; + } + } + + AttachmentUsage usage = new AttachmentUsage(Source.releaseId(releaseId), attachmentContentId, Source.projectId(projectId)); + final UsageData usageData; + switch (UsageData._Fields.findByName(type)) { + case LICENSE_INFO: + LicenseInfoUsage licenseInfoUsage = new LicenseInfoUsage(Collections.emptySet()); + licenseInfoUsage.setIncludeConcludedLicense(includeConludedLicense.contains(s)); + if (projectPath != null) { + licenseInfoUsage.setProjectPath(projectPath); + } + usageData = UsageData.licenseInfo(licenseInfoUsage); + break; + case SOURCE_PACKAGE: + usageData = UsageData.sourcePackage(new SourcePackageUsage()); + break; + case MANUALLY_SET: + usageData = UsageData.manuallySet(new ManuallySetUsage()); + break; + default: + throw new IllegalArgumentException("Unexpected UsageData type: " + type); + } + usage.setUsageData(usageData); + return usage; + } + + /** + * Here, "equivalent" means an AttachmentUsage should replace another one in the DB, not that they are equal. + * I.e, they have the same attachmentContentId, owner, usedBy, and same UsageData type. + */ + @NotNull + public Predicate isUsageEquivalent(AttachmentUsage usage) { + return equivalentUsage -> usage.getAttachmentContentId().equals(equivalentUsage.getAttachmentContentId()) && + usage.getOwner().equals(equivalentUsage.getOwner()) && + usage.getUsedBy().equals(equivalentUsage.getUsedBy()) && + usage.getUsageData().getSetField().equals(equivalentUsage.getUsageData().getSetField()); + } + + public void deleteAttachmentUsages(List usagesToDelete) throws TException { + ThriftClients thriftClients = new ThriftClients(); + AttachmentService.Iface attachmentClient = thriftClients.makeAttachmentClient(); + attachmentClient.deleteAttachmentUsages(usagesToDelete); + } + + public void makeAttachmentUsages(List usagesToCreate) throws TException { + ThriftClients thriftClients = new ThriftClients(); + AttachmentService.Iface attachmentClient = thriftClients.makeAttachmentClient(); + attachmentClient.makeAttachmentUsages(usagesToCreate); + } + + public List getUsedAttachments(Source usedBy, Object object) throws TException { + ThriftClients thriftClients = new ThriftClients(); + AttachmentService.Iface attachmentClient = thriftClients.makeAttachmentClient(); + List allUsagesByProjectAfterCleanUp = attachmentClient.getUsedAttachments(usedBy, null); + return allUsagesByProjectAfterCleanUp; + } + public String getCyclicLinkedProjectPath(Project project, User user) throws TException { ProjectService.Iface sw360ProjectClient = getThriftProjectClient(); String cyclicLinkedProjectPath = sw360ProjectClient.getCyclicLinkedProjectPath(project, user); 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 ffc78220a6..2f8296c1e1 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 @@ -356,6 +356,7 @@ public void before() throws TException, IOException { projectList.add(project2); Map linkedReleases2 = new HashMap<>(); + Map linkedReleases3 = new HashMap<>(); Map linkedProjects2 = new HashMap<>(); Map linkedProjects3 = new HashMap<>(); Project project4 = new Project(); @@ -410,6 +411,32 @@ public void before() throws TException, IOException { release5.setVersion("2.3.1"); release5.setCreatedOn("2016-12-28"); + Project project9 = new Project(); + project9.setId("0000007"); + project9.setName("attachUsages"); + project9.setVersion("2"); + project9.setCreatedBy(testUserId); + project9.setProjectType(ProjectType.PRODUCT); + project9.setState(ProjectState.ACTIVE); + project9.setClearingState(ProjectClearingState.OPEN); + linkedReleases3.put("00000071", projectReleaseRelationship); + project9.setReleaseIdToUsage(linkedReleases3); + + Release release9 = new Release(); + release9.setId("00000071"); + release9.setName("docker"); + release9.setCpeid("cpe:/a:Google:Angular:2.3.1:"); + release9.setReleaseDate("2024-03-17"); + release9.setVersion("2"); + release9.setCreatedOn("2024-03-28"); + release9.setAttachments(setOfAttachment); + + List attachmentUsageNewList = new ArrayList<>(); + List deselectedUsagesFromRequest = new ArrayList<>(); + List selectedUsagesFromRequest = new ArrayList<>(); + List selectedUsages = Arrays.asList("00000071_sourcePackage_1234"); + + Set releaseIds2 = new HashSet<>(Collections.singletonList("00000071")); Set releaseIds = new HashSet<>(Collections.singletonList("3765276512")); Set releaseIdsTransitive = new HashSet<>(Arrays.asList("3765276512", "5578999")); @@ -467,6 +494,11 @@ public void before() throws TException, IOException { 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(project7.getId()), any())).willReturn(project7); + given(this.projectServiceMock.getProjectForUserById(eq(project9.getId()), any())).willReturn(project9); + given(this.projectServiceMock.getUsedAttachments(any(), any())).willReturn(attachmentUsageNewList); + given(this.projectServiceMock.validate(any(), any(), any(), any())).willReturn(true); + given(this.projectServiceMock.deselectedAttachmentUsagesFromRequest(any(), eq(selectedUsages), any(), any(), any())).willReturn(deselectedUsagesFromRequest); + given(this.projectServiceMock.selectedAttachmentUsagesFromRequest(any(), eq(selectedUsages), any(), any(), any())).willReturn(selectedUsagesFromRequest); 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); @@ -477,6 +509,7 @@ public void before() throws TException, IOException { given(this.projectServiceMock.searchProjectByGroup(any(), any())).willReturn(new ArrayList(projectList)); given(this.projectServiceMock.refineSearch(any(), any())).willReturn(projectListByName); given(this.projectServiceMock.getReleaseIds(eq(project.getId()), any(), eq(false))).willReturn(releaseIds); + given(this.projectServiceMock.getReleaseIds(eq(project9.getId()), any(), eq(true))).willReturn(releaseIds2); given(this.projectServiceMock.getReleaseIds(eq(project.getId()), any(), eq(true))).willReturn(releaseIdsTransitive); given(this.projectServiceMock.deleteProject(eq(project.getId()), any())).willReturn(RequestStatus.SUCCESS); given(this.projectServiceMock.updateProjectReleaseRelationship(any(), any(), any())).willReturn(projectReleaseRelationshipResponseBody); @@ -1710,6 +1743,22 @@ public void should_document_link_projects() throws Exception { ))); } + @Test + public void should_document_save_usages() throws Exception { + MockHttpServletRequestBuilder requestBuilder = post("/api/projects/" + "0000007" + "/saveAttachmentUsages"); + Map> usages = Map.of( + "selected", new ArrayList<>(List.of("00000071_sourcePackage_1234")), + "deselected", new ArrayList<>(List.of()), + "selectedConcludedUsages", new ArrayList<>(List.of()), + "deselectedConcludedUsages", new ArrayList<>(List.of())); + + String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword); + this.mockMvc.perform(requestBuilder.contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(usages)) + .header("Authorization", "Bearer " + accessToken)) + .andExpect(status().isCreated()); + } + @Test public void should_document_upload_attachment_to_project() throws Exception { testAttachmentUpload("/api/projects/", project.getId());