diff --git a/rest/resource-server/src/docs/asciidoc/projects.adoc b/rest/resource-server/src/docs/asciidoc/projects.adoc index 007becec8c..7608c22f7b 100644 --- a/rest/resource-server/src/docs/asciidoc/projects.adoc +++ b/rest/resource-server/src/docs/asciidoc/projects.adoc @@ -1005,3 +1005,16 @@ include::{snippets}/should_document_create_clearing_request/curl-request.adoc[] ===== Example response include::{snippets}/should_document_create_clearing_request/http-response.adoc[] +[[resources-project-get-download-project-clearing-report]] +==== Export Create Project Clearing Report + +A `GET` request is used to export Create Project Clearing Report. + +===== Request parameter +include::{snippets}/should_document_get_export_project_create_clearing_request/request-parameters.adoc[] + +===== Example request +include::{snippets}/should_document_get_export_project_create_clearing_request/curl-request.adoc[] + +===== Example response +include::{snippets}/should_document_get_export_project_create_clearing_request/http-response.adoc[] \ No newline at end of file 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 fa025b1cbe..25e6a4f93f 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 @@ -14,7 +14,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; @@ -34,7 +33,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.thrift.TException; @@ -68,7 +67,6 @@ import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage; import org.eclipse.sw360.datahandler.thrift.attachments.UsageData; import org.eclipse.sw360.datahandler.thrift.components.ClearingState; -import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.components.ReleaseClearingStateSummary; import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink; @@ -95,7 +93,6 @@ import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ProjectVulnerabilityRating; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityCheckStatus; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityDTO; -import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilitySummary; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityRatingForProject; import org.eclipse.sw360.rest.resourceserver.attachment.Sw360AttachmentService; import org.eclipse.sw360.rest.resourceserver.component.Sw360ComponentService; @@ -109,10 +106,7 @@ import org.eclipse.sw360.rest.resourceserver.release.ReleaseController; import org.eclipse.sw360.rest.resourceserver.release.Sw360ReleaseService; import org.eclipse.sw360.rest.resourceserver.user.Sw360UserService; -import org.eclipse.sw360.rest.resourceserver.user.UserController; -import org.eclipse.sw360.rest.resourceserver.vendor.VendorController; import org.eclipse.sw360.rest.resourceserver.vulnerability.Sw360VulnerabilityService; -import org.eclipse.sw360.rest.resourceserver.vulnerability.VulnerabilityController; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.json.GsonJsonParser; @@ -141,8 +135,8 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -162,7 +156,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.TreeMap; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -178,7 +171,6 @@ @RequiredArgsConstructor(onConstructor = @__(@Autowired)) @RestController @SecurityRequirement(name = "tokenAuth") -@SecurityRequirement(name = "basic") public class ProjectController implements RepresentationModelProcessor { private static final String CREATED_BY = "createdBy"; private static final String ATTACHMENT_TYPE = "attachmentType"; @@ -483,7 +475,7 @@ public ResponseEntity> getLinkedProject(Pageable pa boolean isTransitive = Boolean.parseBoolean(transitive); Map linkedProjects = sw360Proj.getLinkedProjects(); - List keys = linkedProjects != null ? new ArrayList<>(linkedProjects.keySet()) : new ArrayList<>(); + List keys = new ArrayList<>(linkedProjects.keySet()); List projects = keys.stream().map(projId -> wrapTException(() -> { final Project sw360Project = projectService.getProjectForUserById(projId, sw360User); return sw360Project; @@ -514,44 +506,6 @@ public ResponseEntity> getLinkedProject(Pageable pa return new ResponseEntity<>(resources, status); } - @Operation( - description = "Get releases of linked projects of a single project.", - tags = {"Projects"} - ) - @RequestMapping(value = PROJECTS_URL + "/{id}/linkedProjects/releases", method = RequestMethod.GET) - public ResponseEntity>> getReleasesOfLinkedProject(@Parameter(description = "Project ID", example = "376576") - @PathVariable("id") String id, HttpServletRequest request, Pageable pageable) - throws TException, URISyntaxException, PaginationParameterException, ResourceClassNotFoundException { - - User sw360User = restControllerHelper.getSw360UserFromAuthentication(); - List> result = new ArrayList<>(); - final Set directReleaseIds = projectService.getReleaseIds(id, sw360User, false); - final Set allReleaseIds = projectService.getReleaseIds(id, sw360User, true); - allReleaseIds.removeAll(directReleaseIds); - - List releases = allReleaseIds.stream().map(relId -> wrapTException(() -> { - final Release sw360Release = releaseService.getReleaseForUserById(relId, sw360User); - return sw360Release; - })).collect(Collectors.toList()); - - if (releases.isEmpty()) { - CollectionModel> emptyModel = CollectionModel.empty(); - return ResponseEntity.status(HttpStatus.NO_CONTENT).body(emptyModel); - } - PaginationResult paginationResult = restControllerHelper.createPaginationResult(request, pageable, releases, SW360Constants.TYPE_RELEASE); - - final List> releaseResources = paginationResult.getResources().stream() - .map(sw360Release -> wrapTException(() -> { - final Release embeddedRelease = restControllerHelper.convertToEmbeddedLinkedProjectsReleases(sw360Release); - final HalResource releaseResource = new HalResource<>(embeddedRelease); - return releaseResource; - })).collect(Collectors.toList()); - - CollectionModel resources = restControllerHelper.generatePagesResource(paginationResult, releaseResources);; - HttpStatus status = resources == null ? HttpStatus.NO_CONTENT : HttpStatus.OK; - return new ResponseEntity<>(resources, status); - } - @Operation( description = "Delete a single project.", tags = {"Projects"}, @@ -735,9 +689,9 @@ public ResponseEntity linkToProjects( for(String projId: projectIdsInRequestBody) { Project proj = projectService.getProjectForUserById(projId, sw360User); - Map linkedProject = Optional.ofNullable(proj.getLinkedProjects()).orElse(new HashMap<>()); + Map linkedProject= proj.getLinkedProjects(); - if (linkedProject.keySet().contains(id)) { + if (proj.getLinkedProjects().keySet().contains(id)) { alreadyLinkedIds.add(projId); continue; } @@ -1018,99 +972,6 @@ public ResponseEntity>> getECCsOfReleases( return new ResponseEntity<>(resources, status); } - @Operation( - description = "Get vulnerabilities of all projects including parent and directly linked projects.", - tags = {"Projects"} - ) - - @RequestMapping(value = PROJECTS_URL + "/{id}/vulnerabilitySummary", method = RequestMethod.GET) - public ResponseEntity>> getAllVulnerabilities( - Pageable pageable, - HttpServletRequest request, - @PathVariable("id") String id) - throws TException, PaginationParameterException, ResourceClassNotFoundException, URISyntaxException { - - User sw360User = restControllerHelper.getSw360UserFromAuthentication(); - Project sw360Project = projectService.getProjectForUserById(id, sw360User); - - List parentProjectVulnerabilities = vulnerabilityService.getVulnerabilitiesByProjectId(id, sw360User); - - Map> vulnerabilitiesMap = new HashMap<>(); - String parentProjectKey = sw360Project.getName() + " " + sw360Project.getVersion(); - vulnerabilitiesMap.put(parentProjectKey, parentProjectVulnerabilities); - - if (!sw360Project.getLinkedProjects().isEmpty()) { - for (String linkedProjectId : sw360Project.getLinkedProjects().keySet()) { - Project linkedProject = projectService.getProjectForUserById(linkedProjectId, sw360User); - List linkedProjectVulnerabilities = vulnerabilityService.getVulnerabilitiesByProjectId(linkedProjectId, sw360User); - String linkedProjectKey = linkedProject.getName() + " " + linkedProject.getVersion(); - vulnerabilitiesMap.put(linkedProjectKey, linkedProjectVulnerabilities); - } - } - - List newList = new ArrayList<>(); - Optional projectVulnerabilityRating = wrapThriftOptionalReplacement(vulnerabilityService.getProjectVulnerabilityRatingByProjectId(id, sw360User)); - Map>> vulnerabilityIdToStatusHistory = projectVulnerabilityRating - .map(ProjectVulnerabilityRating::getVulnerabilityIdToReleaseIdToStatus).orElseGet(HashMap::new); - - - for (Map.Entry> entry : vulnerabilitiesMap.entrySet()) { - String projectName = entry.getKey(); - List vulnerabilities = entry.getValue(); - - for (VulnerabilityDTO vulnerability : vulnerabilities) { - String comment = "", action = "new"; - Map> vulRatingProj = vulnerabilityService.fillVulnerabilityMetadata(vulnerability, projectVulnerabilityRating); - vulnerability.setProjectRelevance(vulRatingProj.get(vulnerability.externalId).get(vulnerability.intReleaseId).toString()); - Map> relIdToCheckStatus = vulnerabilityIdToStatusHistory.get(vulnerability.externalId); - if(null != relIdToCheckStatus && relIdToCheckStatus.containsKey(vulnerability.intReleaseId)) { - List checkStatus = relIdToCheckStatus.get(vulnerability.intReleaseId); - comment = checkStatus.get(checkStatus.size()-1).getComment(); - action = checkStatus.get(checkStatus.size()-1).getProjectAction(); - } - vulnerability.setComment(comment); - vulnerability.setAction(action); - VulnerabilitySummary summ = new VulnerabilitySummary(); - summ.setProjectName(projectName); - summ.setExternalId(vulnerability.getExternalId()); - summ.setDescription(vulnerability.getDescription()); - summ.setTitle(vulnerability.getTitle()); - summ.setPriority(vulnerability.getPriority()); - summ.setPriorityToolTip(vulnerability.getPriorityToolTip()); - summ.setAction(vulnerability.getAction()); - summ.setComment(vulnerability.getComment()); - summ.setMatchedBy(vulnerability.getMatchedBy()); - summ.setUsedNeedle(vulnerability.getUsedNeedle()); - summ.setProjectRelevance(vulnerability.getProjectRelevance()); - summ.setIntReleaseId(vulnerability.getIntReleaseId()); - summ.setIntReleaseName(vulnerability.getIntReleaseName()); - newList.add(summ); - } - } - - PaginationResult paginationResult = restControllerHelper.createPaginationResult(request, pageable, newList, SW360Constants.TYPE_VULNERABILITYSUMMARY); - - final List> vulResources = paginationResult.getResources().stream() - .map(sw360Vul -> wrapTException(() -> { - final VulnerabilitySummary embeddedVul = restControllerHelper.convertToEmbeddedVulnerabilitySumm(sw360Vul); - final HalResource vulResource = new HalResource<>(embeddedVul); - Link projectLink = linkTo(VulnerabilityController.class) - .slash("api/vulnerabilities/" + sw360Vul.getExternalId()).withSelfRel(); - vulResource.add(projectLink); - return vulResource; - })).collect(Collectors.toList()); - - CollectionModel resources; - if (vulResources.size() == 0) { - resources = restControllerHelper.emptyPageResource(Project.class, paginationResult); - } else { - resources = restControllerHelper.generatePagesResource(paginationResult, vulResources); - } - - HttpStatus status = resources == null ? HttpStatus.NO_CONTENT : HttpStatus.OK; - return new ResponseEntity<>(resources, status); - } - @Operation( description = "Get vulnerabilities of a single project.", tags = {"Projects"} @@ -1347,126 +1208,6 @@ public ResponseEntity>> getLicensesOfReleas return new ResponseEntity<>(resources, status); } - @Operation( - summary = "Download license info for the project.", - description = "Set the request parameter `&template=` for variant `REPORT` to choose " + - "specific template.", - tags = {"Projects"} - ) - @RequestMapping(value = PROJECTS_URL + "/{id}/licenseinfo", method = RequestMethod.GET) - public void downloadLicenseInfo( - @Parameter(description = "Project ID.", example = "376576") - @PathVariable("id") String id, - @Parameter(description = "Output generator class", - schema = @Schema(type = "string", - allowableValues = {"DocxGenerator", "XhtmlGenerator", - "TextGenerator"} - )) - @RequestParam("generatorClassName") String generatorClassName, - @Parameter(description = "Variant of the report", - schema = @Schema(implementation = OutputFormatVariant.class)) - @RequestParam("variant") String variant, - @Parameter(description = "The external Ids of the project", example = "376577") - @RequestParam(value = "externalIds", required = false) String externalIds, - @RequestParam(value = "template", required = false) String template, - @Parameter(description = "Generate license info including all attachments of the linked releases") - @RequestParam(value = "includeAllAttachments", required = false ) boolean includeAllAttachments, - HttpServletResponse response - ) throws TException, IOException { - final User sw360User = restControllerHelper.getSw360UserFromAuthentication(); - final Project sw360Project = projectService.getProjectForUserById(id, sw360User); - List mappedProjectLinks = new ArrayList<>(); - - if (includeAllAttachments) { - mappedProjectLinks = projectService.createLinkedProjects(sw360Project, - projectService.filterAndSortAllAttachments(SW360Constants.INITIAL_LICENSE_INFO_ATTACHMENT_TYPES), true, sw360User); - } else { - mappedProjectLinks = projectService.createLinkedProjects(sw360Project, - projectService.filterAndSortAttachments(SW360Constants.LICENSE_INFO_ATTACHMENT_TYPES), true, sw360User); - } - - List attchmntUsg = attachmentService.getAttachemntUsages(id); - - Map> releaseIdToExcludedLicenses = attchmntUsg.stream() - .collect(Collectors.toMap(AttachmentUsage::getOwner, - x -> x.getUsageData().getLicenseInfo().getExcludedLicenseIds(), (li1, li2) -> li1)); - - Map usedAttachmentContentIds = attchmntUsg.stream() - .collect(Collectors.toMap(AttachmentUsage::getAttachmentContentId, attUsage -> { - if (attUsage.isSetUsageData() - && attUsage.getUsageData().getSetField().equals(UsageData._Fields.LICENSE_INFO)) { - return Boolean.valueOf(attUsage.getUsageData().getLicenseInfo().isIncludeConcludedLicense()); - } - return Boolean.FALSE; - }, (li1, li2) -> li1)); - - final Map> selectedReleaseAndAttachmentIds = new HashMap<>(); - final Map> excludedLicensesPerAttachments = new HashMap<>(); - - - mappedProjectLinks.forEach(projectLink -> wrapTException(() -> - projectLink.getLinkedReleases().stream().filter(ReleaseLink::isSetAttachments).forEach(releaseLink -> { - String releaseLinkId = releaseLink.getId(); - Set excludedLicenseIds = releaseIdToExcludedLicenses.get(Source.releaseId(releaseLinkId)); - - if (!selectedReleaseAndAttachmentIds.containsKey(releaseLinkId)) { - selectedReleaseAndAttachmentIds.put(releaseLinkId, new HashMap<>()); - } - final List attachments = releaseLink.getAttachments(); - Release release = componentService.getReleaseById(releaseLinkId, sw360User); - for (final Attachment attachment : attachments) { - String attachemntContentId = attachment.getAttachmentContentId(); - if (includeAllAttachments) { - selectedReleaseAndAttachmentIds.get(releaseLinkId).put(attachemntContentId, - false); - } else { - if (usedAttachmentContentIds.containsKey(attachemntContentId)) { - boolean includeConcludedLicense = usedAttachmentContentIds.get(attachemntContentId); - List licenseInfoParsingResult = licenseInfoService - .getLicenseInfoForAttachment(release, sw360User, attachemntContentId, includeConcludedLicense); - excludedLicensesPerAttachments.put(attachemntContentId, getExcludedLicenses(excludedLicenseIds, licenseInfoParsingResult)); - selectedReleaseAndAttachmentIds.get(releaseLinkId).put(attachemntContentId, includeConcludedLicense); - } - } - } - }))); - - final String projectName = sw360Project.getName(); - final String projectVersion = sw360Project.getVersion(); - final String timestamp = SW360Utils.getCreatedOnTime().replaceAll("\\s", "_").replace(":", "_"); - String outputGeneratorClassNameWithVariant = generatorClassName+"::"+variant; - final OutputFormatInfo outputFormatInfo = licenseInfoService.getOutputFormatInfoForGeneratorClass(generatorClassName); - final String filename = String.format("%s-%s%s-%s.%s", Strings.nullToEmpty(variant).equals("DISCLOSURE") ? "LicenseInfo" : "ProjectClearingReport", projectName, - StringUtils.isBlank(projectVersion) ? "" : "-" + projectVersion, timestamp, - outputFormatInfo.getFileExtension()); - - String fileName = ""; - if (CommonUtils.isNotNullEmptyOrWhitespace(template) && CommonUtils.isNotNullEmptyOrWhitespace(REPORT_FILENAME_MAPPING)) { - Map orgToTemplate = Arrays.stream(REPORT_FILENAME_MAPPING.split(",")) - .collect(Collectors.toMap(k -> k.split(":")[0], v -> v.split(":")[1])); - fileName = orgToTemplate.get(template); - } - - final LicenseInfoFile licenseInfoFile = licenseInfoService.getLicenseInfoFile(sw360Project, sw360User, - outputGeneratorClassNameWithVariant, selectedReleaseAndAttachmentIds, excludedLicensesPerAttachments, - externalIds, fileName); - byte[] byteContent = licenseInfoFile.bufferForGeneratedOutput().array(); - response.setContentType(outputFormatInfo.getMimeType()); - response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", filename)); - FileCopyUtils.copy(byteContent, response.getOutputStream()); - } - - private Set getExcludedLicenses(Set excludedLicenseIds, - List licenseInfoParsingResult) { - - Predicate filteredLicense = licenseNameWithText -> excludedLicenseIds - .contains(licenseNameWithText.getLicenseName()); - Function> streamLicenseNameWithTexts = licenseInfo -> licenseInfo - .getLicenseNamesWithTexts().stream(); - return licenseInfoParsingResult.stream().map(LicenseInfoParsingResult::getLicenseInfo) - .flatMap(streamLicenseNameWithTexts).filter(filteredLicense).collect(Collectors.toSet()); - } - @Operation( description = "Get all attachment information of a project.", tags = {"Projects"} @@ -1707,7 +1448,6 @@ public ResponseEntity attachmentUsages(@Parameter(description = "Project ID.") @ Map attachmentUsageMap = parser.parseMap(serializedUsages); List> listOfAttachmentUsages = (List>) attachmentUsageMap .get(SW360_ATTACHMENT_USAGES); - listOfAttachmentUsages.removeIf(item -> item == null); for (Map attachmentUsage : listOfAttachmentUsages) { attachmentUsage.remove("revision"); attachmentUsage.remove("type"); @@ -1777,12 +1517,11 @@ private List> getReleaseObjectMapper(List linksValeToKeep = new LinkedHashMap<>(); - linksValeToKeep.put("self", releaseLink); - valueToKeep.put("_links", linksValeToKeep); + valueToKeep.put("_links", releaseLink); modifiedList.add(valueToKeep); } return modifiedList; @@ -2320,28 +2059,16 @@ private Project convertToProject(Map requestBody) { mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModule(sw360Module); - if (requestBody.containsKey(mapOfProjectFieldsToRequestBody.get(Project._Fields.VISBILITY))) { - try { - String visibility = (String) requestBody - .get(mapOfProjectFieldsToRequestBody.get(Project._Fields.VISBILITY)); - requestBody.put(mapOfProjectFieldsToRequestBody.get(Project._Fields.VISBILITY), - visibility.toUpperCase()); - } catch (IllegalArgumentException e) { - System.err.println("Error processing visibility field: " + e.getMessage()); - System.err.println("Failed requestBody: " + requestBody); - throw e; - } - } - if (requestBody.containsKey("linkedProjects")) { Map linkedProjects = (Map) requestBody.get("linkedProjects"); linkedProjects.entrySet().stream().forEach(entry -> { if (entry.getValue() instanceof String) { - Map projectRelationshipMap = new HashMap<>(); - projectRelationshipMap.put("projectRelationship", entry.getValue()); - linkedProjects.put(entry.getKey(), projectRelationshipMap); + Map projectProjectRelationShip = new HashMap(); + projectProjectRelationShip.put("projectRelationship", entry.getValue()); + linkedProjects.put(entry.getKey(), projectProjectRelationShip); } }); + } return mapper.convertValue(requestBody, Project.class); } @@ -2423,25 +2150,21 @@ public ResponseEntity getLicObligations(Pageable pageable, private Map createPaginationMetadata(Pageable pageable, Map licenseObligation) { List> entries = new ArrayList<>(licenseObligation.entrySet()); - boolean isDefaultPaged = pageable != null && pageable.getPageSize() == 20 && pageable.getPageNumber() == 0; - boolean isPaged = pageable != null && pageable.isPaged() && !isDefaultPaged; - int pageSize = isPaged ? pageable.getPageSize() : entries.size(); - int pageNumber = isPaged ? pageable.getPageNumber() : 0; + int pageSize = pageable.getPageSize(); + int pageNumber = pageable.getPageNumber(); int start = pageNumber * pageSize; int end = Math.min(start + pageSize, entries.size()); - - List> paginatedEntries = entries.subList(start, end); + entries = entries.subList(start, end); int totalPages = (int) Math.ceil((double) licenseObligation.size()/ pageSize); - Map paginatedMap = new LinkedHashMap<>(); - for (Map.Entry entry : paginatedEntries) { + for (Map.Entry entry : entries) { paginatedMap.put(entry.getKey(), entry.getValue()); } Map pagination = Map.of( - "size", pageSize, - "totalElements", licenseObligation.size(), - "totalPages", isPaged ? totalPages : 1, - "number", pageNumber + "size", pageSize, + "totalElements", licenseObligation.size(), + "totalPages", totalPages, + "number", pageNumber ); Map responseBody = new HashMap<>(); responseBody.put("page", pagination); @@ -2541,43 +2264,13 @@ public ResponseEntity addLicenseObligations( } } selectedLicenseObligation.putAll(obligationStatusMapFromReport); - RequestStatus requestStatus= projectService.addLinkedObligations(sw360Project, sw360User, selectedLicenseObligation); + RequestStatus requestStatus= projectService.updateLinkedObligations(sw360Project, sw360User, selectedLicenseObligation); if (requestStatus == RequestStatus.SUCCESS) { return new ResponseEntity<>("License Obligation Added Successfully", HttpStatus.CREATED); } return new ResponseEntity<>("Failed to add/update obligation for project", HttpStatus.NOT_FOUND); } - @PreAuthorize("hasAuthority('WRITE')") - @Operation( - summary = "Update License Obligations ", - description = "Pass a map of obligations in request body.", - tags = {"Projects"} - ) - @RequestMapping(value = PROJECTS_URL + "/{id}/updateLicenseObligation", method = RequestMethod.PATCH) - public ResponseEntity patchLicenseObligations( - @Parameter(description = "Project ID.") - @PathVariable("id") String id, - @Parameter(description = "Map of obligation status info.") - @RequestBody Map requestBodyObligationStatusInfo - ) throws TException { - final User sw360User = restControllerHelper.getSw360UserFromAuthentication(); - final Project sw360Project = projectService.getProjectForUserById(id, sw360User); - ObligationList obligation = new ObligationList(); - Map obligationStatusMap = Maps.newHashMap(); - if (CommonUtils.isNotNullEmptyOrWhitespace(sw360Project.getLinkedObligationId())) { - obligation = projectService.getObligationData(sw360Project.getLinkedObligationId(), sw360User); - obligationStatusMap = CommonUtils.nullToEmptyMap(obligation.getLinkedObligationStatus()); - } - Map updatedObligationStatusMap = projectService - .compareObligationStatusMap(sw360User, obligationStatusMap, requestBodyObligationStatusInfo); - RequestStatus updateObligationStatus = projectService.patchLinkedObligations(sw360User, updatedObligationStatusMap, obligation); - if (updateObligationStatus == RequestStatus.SUCCESS) { - return new ResponseEntity<>("License Obligation Updated Successfully", HttpStatus.CREATED); - } - return new ResponseEntity<>("Cannot update License Obligation", HttpStatus.CONFLICT); - } - @Operation( description = "Get summary and administration page of project tab.", tags = {"Projects"} @@ -2608,19 +2301,13 @@ public ResponseEntity> getAdministration( private void setAdditionalFieldsToHalResource(Project sw360Project, HalResource userHalResource) throws TException { try { - String modifiedByEmail = sw360Project.getModifiedBy(); - if (modifiedByEmail != null) { - User projectModifier = restControllerHelper.getUserByEmail(modifiedByEmail); - if (projectModifier != null) { - restControllerHelper.addEmbeddedUser(userHalResource, projectModifier, "modifiedBy"); - } + User projectModifier = restControllerHelper.getUserByEmail(sw360Project.getModifiedBy()); + if (projectModifier != null && projectModifier.getEmail() != null) { + restControllerHelper.addEmbeddedUser(userHalResource, projectModifier, "modifiedBy"); } - String projectOwnerEmail = sw360Project.getProjectOwner(); - if (projectOwnerEmail != null) { - User projectOwner = restControllerHelper.getUserByEmail(sw360Project.getProjectOwner()); - if (projectOwner != null) { - restControllerHelper.addEmbeddedUser(userHalResource, projectOwner, "projectOwner"); - } + User projectOwner = restControllerHelper.getUserByEmail(sw360Project.getProjectOwner()); + if (projectOwner != null && projectOwner.getEmail() != null) { + restControllerHelper.addEmbeddedUser(userHalResource, projectOwner, "projectOwner"); } if (sw360Project.getSecurityResponsibles() == null || sw360Project.getSecurityResponsibles().isEmpty()) { sw360Project.setSecurityResponsibles(new HashSet(){{add("");}}); 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 8e5f863adc..8f2d46c04b 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 @@ -751,7 +751,7 @@ public Function createProjectLinkMapper(Function createLinkedProjects(Project project, + public List createLinkedProjects(Project project, Function projectLinkMapper, boolean deep, User user) { final Collection linkedProjects = SW360Utils .flattenProjectLinkTree(SW360Utils.getLinkedProjects(project, deep, new ThriftClients(), log, user)); diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportController.java index d725c67e55..ecff1b7c86 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportController.java @@ -49,10 +49,10 @@ @SecurityRequirement(name = "basic") public class SW360ReportController implements RepresentationModelProcessor { private static final String COMPONENTS = "components"; - private static final String PROJECTS = "projects"; - private static final String LICENSES = "licenses"; + private static final String LICENSE_INFO = "licenseInfo"; + private static final String EXPORT_CREATE_PROJ_CLEARING_REPORT = "exportCreateProjectClearingReport"; public static final String REPORTS_URL = "/reports"; @@ -104,7 +104,13 @@ public void getProjectReport( request, sw360User, module); break; case LICENSES: - getLicensesReports(response, sw360User, module); + getLicensesReports(request, response, sw360User, module); + break; + case LICENSE_INFO: + getLicensesInfoReports(request, response, sw360User, module, projectId); + break; + case EXPORT_CREATE_PROJ_CLEARING_REPORT: + exportProjectCreateClearingRequest(request, response, sw360User, module, projectId); break; default: break; @@ -126,7 +132,7 @@ private void getProjectReports(boolean withLinkedReleases, boolean mailRequest, responseJson.addProperty("response", "The downloaded report link will be send to the end user."); response.getWriter().write(responseJson.toString()); } else { - downloadExcelReport(withLinkedReleases, response, sw360User, module, projectId); + downloadExcelReport(withLinkedReleases, request, response, sw360User, module, projectId); } } catch (Exception e) { throw new TException(e.getMessage()); @@ -142,22 +148,38 @@ private void getComponentsReports(boolean withLinkedReleases, boolean mailReques responseJson.addProperty("response", "Component report download link will get send to the end user."); response.getWriter().write(responseJson.toString()); } else { - downloadExcelReport(withLinkedReleases, response, sw360User, module, null); + downloadExcelReport(withLinkedReleases, request, response, sw360User, module, null); } } catch (Exception e) { throw new TException(e.getMessage()); } } - private void getLicensesReports(HttpServletResponse response, User sw360User, String module) throws TException { + private void getLicensesReports(HttpServletRequest request, HttpServletResponse response, User sw360User, String module) throws TException { try { - downloadExcelReport(false, response, sw360User, module, null); + downloadExcelReport(false, request, response, sw360User, module, null); } catch (Exception e) { throw new TException(e.getMessage()); } } - private void downloadExcelReport(boolean withLinkedReleases, HttpServletResponse response, User user, String module, String projectId) + private void getLicensesInfoReports(HttpServletRequest request, HttpServletResponse response, User sw360User, String module, String projectId) throws TException { + try { + downloadExcelReport(false, request, response, sw360User, module, projectId); + }catch (Exception e) { + throw new TException(e.getMessage()); + } + } + + private void exportProjectCreateClearingRequest(HttpServletRequest request, HttpServletResponse response, User sw360User, String module, String projectId) throws TException { + try { + downloadExcelReport(false, request, response, sw360User, module, projectId); + }catch (Exception e) { + throw new TException(e.getMessage()); + } + } + + private void downloadExcelReport(boolean withLinkedReleases, HttpServletRequest request , HttpServletResponse response, User user, String module, String projectId) throws TException { try { ByteBuffer buffer = null; @@ -171,6 +193,14 @@ private void downloadExcelReport(boolean withLinkedReleases, HttpServletResponse case LICENSES: buffer = sw360ReportService.getLicenseBuffer(); break; + case LICENSE_INFO: + case EXPORT_CREATE_PROJ_CLEARING_REPORT: + final String generatorClassName = request.getParameter("generatorClassName"); + final String variant = request.getParameter("variant"); + final String template = request.getParameter("template"); + final String externalIds = request.getParameter("externalIds"); + buffer = sw360ReportService.getLicenseInfoBuffer(user, projectId, generatorClassName, variant, template, externalIds); + break; default: break; } @@ -183,6 +213,8 @@ private void downloadExcelReport(boolean withLinkedReleases, HttpServletResponse fileName = String.format("licenses-%s.xlsx", SW360Utils.getCreatedOn()); } else if(module.equals(PROJECTS)) { fileName = sw360ReportService.getDocumentName(user, projectId); + } else if(module.equals(LICENSE_INFO) || module.equals(EXPORT_CREATE_PROJ_CLEARING_REPORT)) { + fileName = sw360ReportService.getGenericLicInfoFileName(request, user); }else { fileName = sw360ReportService.getDocumentName(user, null); } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportService.java index 9c850eba8e..e7b22fdbd3 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/report/SW360ReportService.java @@ -5,28 +5,77 @@ package org.eclipse.sw360.rest.resourceserver.report; import static org.eclipse.sw360.datahandler.common.WrappedException.wrapTException; +import static org.eclipse.sw360.rest.resourceserver.Sw360ResourceServer.REPORT_FILENAME_MAPPING; import java.net.URL; import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.SW360Utils; +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.AttachmentUsage; +import org.eclipse.sw360.datahandler.thrift.attachments.UsageData; import org.eclipse.sw360.datahandler.thrift.components.ComponentService; +import org.eclipse.sw360.datahandler.thrift.components.Release; +import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink; +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.licenses.LicenseService; import org.eclipse.sw360.datahandler.thrift.projects.Project; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink; import org.eclipse.sw360.datahandler.thrift.projects.ProjectService; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.rest.resourceserver.attachment.Sw360AttachmentService; +import org.eclipse.sw360.rest.resourceserver.component.Sw360ComponentService; +import org.eclipse.sw360.rest.resourceserver.license.Sw360LicenseService; +import org.eclipse.sw360.rest.resourceserver.licenseinfo.Sw360LicenseInfoService; +import org.eclipse.sw360.rest.resourceserver.project.Sw360ProjectService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.google.common.base.Strings; + +import lombok.NonNull; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class SW360ReportService { + @NonNull + private final Sw360ProjectService projectService; + + @NonNull + private final Sw360LicenseService licenseService; + + @NonNull + private final Sw360ComponentService componentService; + + @NonNull + private final Sw360AttachmentService attachmentService; + + @NonNull + private final Sw360LicenseInfoService licenseInfoService; + ThriftClients thriftClients = new ThriftClients(); ProjectService.Iface projectclient = thriftClients.makeProjectClient(); ComponentService.Iface componentclient = thriftClients.makeComponentClient(); @@ -129,4 +178,99 @@ public ByteBuffer getLicenseReportStreamFromURl(String token) public void sendComponentExportSpreadsheetSuccessMail(String emailURL, String email) throws TException { componentclient.sendExportSpreadsheetSuccessMail(emailURL, email); } + + public ByteBuffer getLicenseInfoBuffer(User sw360User, String id, String generatorClassName, String variant, String template, String externalIds) throws TException { + final Project sw360Project = projectService.getProjectForUserById(id, sw360User); + + List mappedProjectLinks = projectService.createLinkedProjects(sw360Project, + projectService.filterAndSortAttachments(SW360Constants.LICENSE_INFO_ATTACHMENT_TYPES), true, sw360User); + + List attchmntUsg = attachmentService.getAttachemntUsages(id); + + Map> releaseIdToExcludedLicenses = attchmntUsg.stream() + .collect(Collectors.toMap(AttachmentUsage::getOwner, + x -> x.getUsageData().getLicenseInfo().getExcludedLicenseIds(), (li1, li2) -> li1)); + + Map usedAttachmentContentIds = attchmntUsg.stream() + .collect(Collectors.toMap(AttachmentUsage::getAttachmentContentId, attUsage -> { + if (attUsage.isSetUsageData() + && attUsage.getUsageData().getSetField().equals(UsageData._Fields.LICENSE_INFO)) { + return Boolean.valueOf(attUsage.getUsageData().getLicenseInfo().isIncludeConcludedLicense()); + } + return Boolean.FALSE; + }, (li1, li2) -> li1)); + + final Map> selectedReleaseAndAttachmentIds = new HashMap<>(); + final Map> excludedLicensesPerAttachments = new HashMap<>(); + + getSelectedAttchIdsAndExcludedLicInfo(sw360User, mappedProjectLinks, releaseIdToExcludedLicenses, + usedAttachmentContentIds, selectedReleaseAndAttachmentIds, excludedLicensesPerAttachments); + + String outputGeneratorClassNameWithVariant = generatorClassName + "::" + variant; + String fileName = ""; + if (CommonUtils.isNotNullEmptyOrWhitespace(template) + && CommonUtils.isNotNullEmptyOrWhitespace(REPORT_FILENAME_MAPPING)) { + Map orgToTemplate = Arrays.stream(REPORT_FILENAME_MAPPING.split(",")) + .collect(Collectors.toMap(k -> k.split(":")[0], v -> v.split(":")[1])); + fileName = orgToTemplate.get(template); + } + final LicenseInfoFile licenseInfoFile = licenseInfoService.getLicenseInfoFile(sw360Project, sw360User, + outputGeneratorClassNameWithVariant, selectedReleaseAndAttachmentIds, excludedLicensesPerAttachments, + externalIds, fileName); + return licenseInfoFile.bufferForGeneratedOutput(); + } + + private void getSelectedAttchIdsAndExcludedLicInfo(User sw360User, List mappedProjectLinks, + Map> releaseIdToExcludedLicenses, Map usedAttachmentContentIds, + final Map> selectedReleaseAndAttachmentIds, + final Map> excludedLicensesPerAttachments) { + mappedProjectLinks.forEach(projectLink -> wrapTException(() -> projectLink.getLinkedReleases().stream() + .filter(ReleaseLink::isSetAttachments).forEach(releaseLink -> { + String releaseLinkId = releaseLink.getId(); + Set excludedLicenseIds = releaseIdToExcludedLicenses.get(Source.releaseId(releaseLinkId)); + + if (!selectedReleaseAndAttachmentIds.containsKey(releaseLinkId)) { + selectedReleaseAndAttachmentIds.put(releaseLinkId, new HashMap<>()); + } + final List attachments = releaseLink.getAttachments(); + Release release = componentService.getReleaseById(releaseLinkId, sw360User); + for (final Attachment attachment : attachments) { + String attachemntContentId = attachment.getAttachmentContentId(); + if (usedAttachmentContentIds.containsKey(attachemntContentId)) { + boolean includeConcludedLicense = usedAttachmentContentIds.get(attachemntContentId); + List licenseInfoParsingResult = licenseInfoService + .getLicenseInfoForAttachment(release, sw360User, attachemntContentId, + includeConcludedLicense); + excludedLicensesPerAttachments.put(attachemntContentId, + getExcludedLicenses(excludedLicenseIds, licenseInfoParsingResult)); + selectedReleaseAndAttachmentIds.get(releaseLinkId).put(attachemntContentId, + includeConcludedLicense); + } + } + }))); + } + + public String getGenericLicInfoFileName(HttpServletRequest request, User sw360User) throws TException { + final String variant = request.getParameter("variant"); + final Project sw360Project = projectService.getProjectForUserById(request.getParameter("projectId"), sw360User); + final String generatorClassName = request.getParameter("generatorClassName"); + final String timestamp = SW360Utils.getCreatedOnTime().replaceAll("\\s", "_").replace(":", "_"); + final OutputFormatInfo outputFormatInfo = licenseInfoService + .getOutputFormatInfoForGeneratorClass(generatorClassName); + return String.format("%s-%s%s-%s.%s", + Strings.nullToEmpty(variant).equals("DISCLOSURE") ? "LicenseInfo" : "ProjectClearingReport", + sw360Project.getName(), + StringUtils.isBlank(sw360Project.getVersion()) ? "" : "-" + sw360Project.getVersion(), timestamp, + outputFormatInfo.getFileExtension()); + } + + private Set getExcludedLicenses(Set excludedLicenseIds, + List licenseInfoParsingResult) { + Predicate filteredLicense = licenseNameWithText -> excludedLicenseIds + .contains(licenseNameWithText.getLicenseName()); + Function> streamLicenseNameWithTexts = licenseInfo -> licenseInfo + .getLicenseNamesWithTexts().stream(); + return licenseInfoParsingResult.stream().map(LicenseInfoParsingResult::getLicenseInfo) + .flatMap(streamLicenseNameWithTexts).filter(filteredLicense).collect(Collectors.toSet()); + } } \ No newline at end of file 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 3064e6c8e9..83afd0a08c 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 @@ -43,10 +43,8 @@ import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoFile; 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.licenses.Obligation; -import org.eclipse.sw360.datahandler.thrift.licenses.ObligationLevel; import org.eclipse.sw360.datahandler.thrift.licenses.ObligationType; import org.eclipse.sw360.datahandler.thrift.packages.Package; import org.eclipse.sw360.datahandler.thrift.packages.PackageManager; @@ -553,6 +551,7 @@ public void before() throws TException, IOException { given(this.projectServiceMock.importCycloneDX(any(),any(),any())).willReturn(requestSummaryForCycloneDX); given(this.sw360ReportServiceMock.getDocumentName(any(), any())).willReturn(projectName); given(this.sw360ReportServiceMock.getProjectBuffer(any(),anyBoolean(),any())).willReturn(ByteBuffer.allocate(10000)); + given(this.sw360ReportServiceMock.getLicenseInfoBuffer(any(), any(), any(), any(), any(), any())).willReturn(ByteBuffer.allocate(10000)); 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); @@ -2057,20 +2056,24 @@ public void should_document_update_project_release_relationship() throws Excepti } @Test - public void should_document_get_download_license_info() throws Exception { - this.mockMvc.perform(get("/api/projects/" + project.getId()+ "/licenseinfo?generatorClassName=XhtmlGenerator&variant=DISCLOSURE&externalIds=portal-id,main-project-id") - .header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) - .accept("application/xhtml+xml")) + public void should_document_get_download_license_info() throws Exception{ + mockMvc.perform(get("/api/reports") + .header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) + .param("module", "licenseInfo") + .param("projectId", project.getId()) + .param("generatorClassName", "XhtmlGenerator") + .param("variant", "DISCLOSURE") + .param("externalIds", "portal-id,main-project-id") + .accept(MediaTypes.HAL_JSON)) .andExpect(status().isOk()) - .andDo(this.documentationHandler - .document(queryParameters( - parameterWithName("generatorClassName") - .description("All possible values for output generator class names are " - + Arrays.asList("DocxGenerator", "XhtmlGenerator", "TextGenerator")), - parameterWithName("variant").description("All the possible values for variants are " - + Arrays.asList(OutputFormatVariant.values())), - parameterWithName("externalIds").description("The external Ids of the project") - ))); + .andDo(this.documentationHandler.document( + queryParameters( + parameterWithName("projectId").description("Id for the project."), + parameterWithName("generatorClassName").description("Projects download format. Possible values are ``"), + parameterWithName("variant").description("All the possible values for variants are ``"), + parameterWithName("externalIds").description("The external Ids of the project"), + parameterWithName("module").description("module represent the project or component. Possible values are ``") + ))); } @Test @@ -2364,6 +2367,27 @@ public void should_document_get_project_licenseclearing_spreadsheet() throws Exc )); } + @Test + public void should_document_get_export_project_create_clearing_request() throws Exception{ + mockMvc.perform(get("/api/reports") + .header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) + .param("module", "exportCreateProjectClearingReport") + .param("projectId", project.getId()) + .param("generatorClassName", "DocxGenerator") + .param("variant", "REPORT") + .param("externalIds", "portal-id,main-project-id") + .accept(MediaTypes.HAL_JSON)) + .andExpect(status().isOk()) + .andDo(this.documentationHandler.document( + requestParameters( + parameterWithName("projectId").description("Id for the project."), + parameterWithName("generatorClassName").description("Projects download format. Possible values are ``"), + parameterWithName("variant").description("The possible values for variants are ``"), + parameterWithName("externalIds").description("The external Ids of the project"), + parameterWithName("module").description("module possible values are ``") + ))); + } + @Test public void should_document_import_cyclonedx() throws Exception { MockMultipartFile file = new MockMultipartFile("file","file=@/sampleBOM.xml".getBytes());