diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java index df1dc36f0d..6eaeb27120 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java @@ -133,9 +133,7 @@ public class SW360Constants { public static final String COMPONENTS = "components"; public static final String PROJECTS = "projects"; public static final String LICENSES = "licenses"; - public static final String REPORTS_URL = "/reports"; public static final String PROJECT_RELEASE_SPREADSHEET_WITH_ECCINFO = "projectReleaseSpreadSheetWithEcc"; - public static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; /** * Hashmap containing the name field for each type. 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 4dcdf2fac3..0fd25f7324 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 @@ -1,15 +1,16 @@ /* -SPDX-FileCopyrightText: © 2023 Siemens AG +SPDX-FileCopyrightText: © 2023-2024 Siemens AG SPDX-License-Identifier: EPL-2.0 */ package org.eclipse.sw360.rest.resourceserver.report; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.eclipse.sw360.datahandler.common.SW360Constants.CONTENT_TYPE_OPENXML_SPREADSHEET; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.List; import jakarta.servlet.http.HttpServletRequest; @@ -21,15 +22,19 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.SW360Utils; +import org.eclipse.sw360.datahandler.thrift.licenseinfo.OutputFormatVariant; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.webmvc.BasePathAwareController; import org.springframework.data.rest.webmvc.RepositoryLinksResource; import org.springframework.hateoas.server.RepresentationModelProcessor; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -46,41 +51,34 @@ @SecurityRequirement(name = "tokenAuth") @SecurityRequirement(name = "basic") public class SW360ReportController implements RepresentationModelProcessor { + public static final String REPORTS_URL = "/reports"; + private static final Logger log = LogManager.getLogger(SW360ReportController.class); private static final String LICENSE_INFO = "licenseInfo"; private static final String LICENSES_RESOURCE_BUNDLE = "licenseResourceBundle"; private static final String ZIP_CONTENT_TYPE = "application/zip"; - private ByteBuffer defaultByteBufferVal = null; private static final String EXPORT_CREATE_PROJ_CLEARING_REPORT = "exportCreateProjectClearingReport"; - - public static final String REPORTS_URL = "/reports"; - - private static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - + private static final List GENERATOR_MODULES = List.of(LICENSE_INFO, EXPORT_CREATE_PROJ_CLEARING_REPORT); @NonNull private final RestControllerHelper restControllerHelper; - @NonNull private final SW360ReportService sw360ReportService; + private final ByteBuffer defaultByteBufferVal = ByteBuffer.wrap(new byte[0]); @Override public RepositoryLinksResource process(RepositoryLinksResource resource) { - resource.add(linkTo(SW360ReportController.class).slash("api/" + SW360Constants.REPORTS_URL).withRel("reports")); + resource.add(linkTo(SW360ReportController.class).slash("api/" + REPORTS_URL).withRel("reports")); return resource; } - private final List mimeTypeList = Arrays.asList("xls", "xlsx"); - @Operation( summary = "Generate the reports.", description = "Generate the reports.", tags = {"Reports"} ) - @GetMapping(value = SW360Constants.REPORTS_URL) + @GetMapping(value = REPORTS_URL) public void getProjectReport( @Parameter(description = "Projects with linked releases.") @RequestParam(value = "withlinkedreleases", required = false, defaultValue = "false") boolean withLinkedReleases, - @Parameter(description = "Report download format.", schema = @Schema(allowableValues = {"xls", "xlsx"})) - @RequestParam(value = "mimetype", required = false, defaultValue = "xlsx") String mimeType, @Parameter(description = "Project id.") @RequestParam(value = "projectId", required = false) String projectId, @Parameter(description = "Module name.", schema = @Schema(allowableValues = { @@ -91,136 +89,198 @@ public void getProjectReport( @RequestParam(value = "module", required = true) String module, @Parameter(description = "Exclude release version from the license info file") @RequestParam(value = "excludeReleaseVersion", required = false, defaultValue = "false") boolean excludeReleaseVersion, + @Parameter(description = "Output generator class. Required for modules [" + LICENSE_INFO + ", " + EXPORT_CREATE_PROJ_CLEARING_REPORT + "]", + schema = @Schema(type = "string", allowableValues = {"DocxGenerator", "XhtmlGenerator", "TextGenerator"})) + @RequestParam(value = "generatorClassName", required = false) String generatorClassName, + @Parameter(description = "Variant of the report. Required for modules [" + LICENSE_INFO + ", " + EXPORT_CREATE_PROJ_CLEARING_REPORT + "]", + schema = @Schema(implementation = OutputFormatVariant.class)) + @RequestParam(value = "variant", required = false) String variant, + @Parameter(description = "Template for generating report. Can be supplied with modules [" + LICENSE_INFO + ", " + EXPORT_CREATE_PROJ_CLEARING_REPORT + "]") + @RequestParam(value = "template", required = false, defaultValue = "") String template, + @Parameter(description = "The external Ids of the project. Can be supplied with modules [" + LICENSE_INFO + ", " + EXPORT_CREATE_PROJ_CLEARING_REPORT + "]", example = "376577") + @RequestParam(value = "externalIds", required = false, defaultValue = "") String externalIds, + @Parameter(description = "Generate report for only current project or with Sub projects. Can be supplied with modules [" + LICENSE_INFO + ", " + EXPORT_CREATE_PROJ_CLEARING_REPORT + "]") + @RequestParam(value = "withSubProject", required = false, defaultValue = "false") boolean withSubProject, HttpServletRequest request, HttpServletResponse response ) throws TException { - + if (GENERATOR_MODULES.contains(module) && (isNullOrEmpty(generatorClassName) || isNullOrEmpty(variant))) { + throw new HttpMessageNotReadableException("Error : GeneratorClassName and Variant is required for module " + module); + } final User sw360User = restControllerHelper.getSw360UserFromAuthentication(); + String baseUrl = getBaseUrl(request); try { - if (validateMimeType(mimeType)) { - switch (module) { + switch (module) { case SW360Constants.PROJECTS: - getProjectReports(withLinkedReleases, response, - request, sw360User, module, projectId, excludeReleaseVersion); + getProjectReports(withLinkedReleases, response, sw360User, module, projectId, + excludeReleaseVersion, baseUrl, generatorClassName, variant, template, externalIds); break; case SW360Constants.COMPONENTS: - getComponentsReports(withLinkedReleases, SW360Constants.MAIL_REQUEST_FOR_COMPONENT_REPORT, response, - request, sw360User, module, excludeReleaseVersion); + getComponentsReports(withLinkedReleases, response, sw360User, module, excludeReleaseVersion, + baseUrl, generatorClassName, variant, template, externalIds); break; case SW360Constants.LICENSES: - getLicensesReports(request, response, sw360User, module, excludeReleaseVersion); + getLicensesReports(response, sw360User, module, excludeReleaseVersion, generatorClassName, variant, + template, externalIds); break; case LICENSE_INFO: - getLicensesInfoReports(request, response, sw360User, module, projectId, excludeReleaseVersion); + getLicensesInfoReports(response, sw360User, module, projectId, excludeReleaseVersion, + generatorClassName, variant, template, externalIds, withSubProject); break; case LICENSES_RESOURCE_BUNDLE: - getLicenseResourceBundleReports(projectId, request, response, sw360User, module, excludeReleaseVersion); + getLicenseResourceBundleReports(projectId, response, sw360User, module, generatorClassName, variant, + template, externalIds, excludeReleaseVersion, withSubProject); break; case SW360Constants.PROJECT_RELEASE_SPREADSHEET_WITH_ECCINFO: - getProjectReleaseWithEccSpreadSheet(response, sw360User, module, projectId, request, excludeReleaseVersion); + getProjectReleaseWithEccSpreadSheet(response, sw360User, module, projectId, excludeReleaseVersion, + generatorClassName, variant, template, externalIds); break; case EXPORT_CREATE_PROJ_CLEARING_REPORT: - exportProjectCreateClearingRequest(request, response, sw360User, module, projectId, excludeReleaseVersion); + exportProjectCreateClearingRequest(response, sw360User, module, projectId, excludeReleaseVersion, + generatorClassName, variant, template, externalIds); break; default: break; - } - } else { - throw new TException("Error : Mimetype either should be : xls/xlsx"); } } catch (Exception e) { throw new TException(e.getMessage()); } } - private void getProjectReports(boolean withLinkedReleases, HttpServletResponse response, - HttpServletRequest request, User sw360User, String module, String projectId, boolean excludeReleaseVersion) throws TException { + private void getProjectReports( + boolean withLinkedReleases, HttpServletResponse response, User sw360User, String module, String projectId, + boolean excludeReleaseVersion, String baseUrl, String generatorClassName, String variant, String template, + String externalIds + ) throws TException { try { if (SW360Constants.MAIL_REQUEST_FOR_PROJECT_REPORT) { - sw360ReportService.getUploadedProjectPath(sw360User, withLinkedReleases,getBaseUrl(request), projectId); + sw360ReportService.getUploadedProjectPath(sw360User, withLinkedReleases, baseUrl, projectId); JsonObject responseJson = new JsonObject(); responseJson.addProperty("response", "The downloaded report link will be send to the end user."); response.getWriter().write(responseJson.toString()); } else { - downloadExcelReport(withLinkedReleases, request, response, sw360User, module, projectId, excludeReleaseVersion, defaultByteBufferVal); + downloadExcelReport(withLinkedReleases, response, sw360User, module, projectId, excludeReleaseVersion, + defaultByteBufferVal, generatorClassName, variant, template, externalIds); } } catch (Exception e) { + log.error(e); throw new TException(e.getMessage()); } } - private void getComponentsReports(boolean withLinkedReleases, boolean mailRequest, HttpServletResponse response, - HttpServletRequest request, User sw360User, String module, boolean excludeReleaseVersion) throws TException { + private void getComponentsReports( + boolean withLinkedReleases, HttpServletResponse response, User sw360User, String module, + boolean excludeReleaseVersion, String baseUrl, String generatorClassName, String variant, String template, + String externalIds + ) throws TException { try { - if (mailRequest) { - sw360ReportService.getUploadedComponentPath(sw360User, withLinkedReleases, getBaseUrl(request)); + if (SW360Constants.MAIL_REQUEST_FOR_COMPONENT_REPORT) { + sw360ReportService.getUploadedComponentPath(sw360User, withLinkedReleases, baseUrl); JsonObject responseJson = new JsonObject(); responseJson.addProperty("response", "Component report download link will get send to the end user."); response.getWriter().write(responseJson.toString()); } else { - downloadExcelReport(withLinkedReleases, request, response, sw360User, module, null, excludeReleaseVersion, defaultByteBufferVal); + downloadExcelReport(withLinkedReleases, response, sw360User, module, null, excludeReleaseVersion, + defaultByteBufferVal, generatorClassName, variant, template, externalIds); } } catch (Exception e) { + log.error(e); + throw new TException(e.getMessage()); + } + } + + private void getLicensesReports( + HttpServletResponse response, User sw360User, String module, boolean excludeReleaseVersion, + String generatorClassName, String variant, String template, String externalIds + ) throws TException { + try { + downloadExcelReport(false, response, sw360User, module, null, excludeReleaseVersion, + defaultByteBufferVal, generatorClassName, variant, template, externalIds); + } catch (Exception e) { + log.error(e); throw new TException(e.getMessage()); } } - private void getLicensesReports(HttpServletRequest request, HttpServletResponse response, - User sw360User, String module, boolean excludeReleaseVersion) throws TException { + private void getLicensesInfoReports( + HttpServletResponse response, User sw360User, String module, String projectId, + boolean excludeReleaseVersion, String generatorClassName, String variant, String template, + String externalIds, boolean withSubProject + ) throws TException { + // TODO: use `withSubProject` while generating LicenseInfo report. try { - downloadExcelReport(false, request, response, sw360User, module, null, excludeReleaseVersion, defaultByteBufferVal); + downloadExcelReport(false, response, sw360User, module, projectId, excludeReleaseVersion, + defaultByteBufferVal, generatorClassName, variant, template, externalIds); } catch (Exception e) { + log.error(e); throw new TException(e.getMessage()); } } - private void getLicensesInfoReports(HttpServletRequest request, HttpServletResponse response, User sw360User, - String module, String projectId, boolean excludeReleaseVersion) throws TException { + private void getProjectReleaseWithEccSpreadSheet( + HttpServletResponse response, User sw360User, String module, String projectId, + boolean excludeReleaseVersion, String generatorClassName, String variant, String template, + String externalIds + ) throws TException { try { - downloadExcelReport(false, request, response, sw360User, module, projectId, excludeReleaseVersion, defaultByteBufferVal); - }catch (Exception e) { + downloadExcelReport(false, response, sw360User, module, projectId, excludeReleaseVersion, + defaultByteBufferVal, generatorClassName, variant, template, externalIds); + } catch (Exception e) { throw new TException(e.getMessage()); } } - private void exportProjectCreateClearingRequest(HttpServletRequest request, HttpServletResponse response, User sw360User, - String module, String projectId, boolean excludeReleaseVersion) throws TException { + private void exportProjectCreateClearingRequest( + HttpServletResponse response, User sw360User, String module, String projectId, + boolean excludeReleaseVersion, String generatorClassName, String variant, String template, + String externalIds + ) throws TException { try { - downloadExcelReport(false, request, response, sw360User, module, projectId, excludeReleaseVersion, defaultByteBufferVal); - }catch (Exception e) { + downloadExcelReport(false, response, sw360User, module, projectId, excludeReleaseVersion, + defaultByteBufferVal, generatorClassName, variant, template, externalIds); + } catch (Exception e) { + log.error(e); throw new TException(e.getMessage()); } } - private void downloadExcelReport(boolean withLinkedReleases, HttpServletRequest request, - HttpServletResponse response, User user, String module, - String projectId, boolean excludeReleaseVersion, ByteBuffer buffer) - throws TException { + private void downloadExcelReport( + boolean withLinkedReleases, HttpServletResponse response, User user, String module, String projectId, + boolean excludeReleaseVersion, ByteBuffer buffer, String generatorClassName, String variant, + String template, String externalIds + ) throws TException { try { ByteBuffer buff = null; + String fileName = sw360ReportService.getDocumentName(user, null, module); + response.setContentType(CONTENT_TYPE_OPENXML_SPREADSHEET); + switch (module) { case SW360Constants.PROJECTS: buff = sw360ReportService.getProjectBuffer(user, withLinkedReleases, projectId); + fileName = sw360ReportService.getDocumentName(user, projectId, module); break; case SW360Constants.COMPONENTS: buff = sw360ReportService.getComponentBuffer(user, withLinkedReleases); break; case SW360Constants.LICENSES: buff = sw360ReportService.getLicenseBuffer(); + fileName = String.format("licenses-%s.xlsx", SW360Utils.getCreatedOn()); break; case LICENSES_RESOURCE_BUNDLE: buff = buffer; + response.setContentType(ZIP_CONTENT_TYPE); + fileName = sw360ReportService.getSourceCodeBundleName(projectId, user); 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"); - buff = sw360ReportService.getLicenseInfoBuffer(user, projectId, generatorClassName, variant, template, externalIds, excludeReleaseVersion); + buff = sw360ReportService.getLicenseInfoBuffer(user, projectId, generatorClassName, variant, + template, externalIds, excludeReleaseVersion); + fileName = sw360ReportService.getGenericLicInfoFileName(user, projectId, generatorClassName, + variant); break; case SW360Constants.PROJECT_RELEASE_SPREADSHEET_WITH_ECCINFO: buff = sw360ReportService.getProjectReleaseSpreadSheetWithEcc(user, projectId); + fileName = sw360ReportService.getDocumentName(user, projectId, module); break; default: break; @@ -228,32 +288,22 @@ private void downloadExcelReport(boolean withLinkedReleases, HttpServletRequest if (null == buff) { throw new TException("No data available for the user " + user.getEmail()); } - response.setContentType(SW360Constants.CONTENT_TYPE); - String fileName; - if (module.equals(SW360Constants.LICENSES)) { - fileName = String.format("licenses-%s.xlsx", SW360Utils.getCreatedOn()); - } else if (module.equals(LICENSES_RESOURCE_BUNDLE)) { - response.setContentType(ZIP_CONTENT_TYPE); - fileName = sw360ReportService.getSourceCodeBundleName(projectId, user); - } else if(module.equals(LICENSE_INFO) || module.equals(EXPORT_CREATE_PROJ_CLEARING_REPORT)) { - fileName = sw360ReportService.getGenericLicInfoFileName(request, user); - } else if ( module.equals(SW360Constants.PROJECTS) || module.equals(SW360Constants.PROJECT_RELEASE_SPREADSHEET_WITH_ECCINFO) ) { - fileName = sw360ReportService.getDocumentName(user, projectId, module); - } else { - fileName = sw360ReportService.getDocumentName(user, null, module); - } response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName)); copyDataStreamToResponse(response, buff); } catch (Exception e) { + log.error(e); throw new TException(e.getMessage()); } } - private void getLicenseResourceBundleReports(String projectId, HttpServletRequest request, - HttpServletResponse response, User sw360User, String module, boolean excludeReleaseVersion) throws TException { + private void getLicenseResourceBundleReports( + String projectId, HttpServletResponse response, User sw360User, String module, String generatorClassName, + String variant, String template, String externalIds, boolean excludeReleaseVersion, boolean withSubProject + ) throws TException { try { - ByteBuffer buffer = sw360ReportService.downloadSourceCodeBundle(projectId, request, sw360User); - downloadExcelReport(false, request, response, sw360User, module, projectId, excludeReleaseVersion, buffer); + ByteBuffer buffer = sw360ReportService.downloadSourceCodeBundle(projectId, sw360User, withSubProject); + downloadExcelReport(false, response, sw360User, module, projectId, excludeReleaseVersion, + buffer, generatorClassName, variant, template, externalIds); } catch (Exception e) { throw new TException(e.getMessage()); } @@ -263,10 +313,6 @@ private void copyDataStreamToResponse(HttpServletResponse response, ByteBuffer b FileCopyUtils.copy(buffer.array(), response.getOutputStream()); } - private boolean validateMimeType(String mimeType) { - return mimeTypeList.contains(mimeType); - } - @Operation( summary = "Download reports.", description = "Download reports.", @@ -274,11 +320,11 @@ private boolean validateMimeType(String mimeType) { responses = {@ApiResponse( responseCode = "200", description = "Generated report.", - content = @Content(mediaType = SW360Constants.CONTENT_TYPE, + content = @Content(mediaType = CONTENT_TYPE_OPENXML_SPREADSHEET, schema = @Schema(type = "string", format = "binary")) )} ) - @GetMapping(value = SW360Constants.REPORTS_URL + "/download") + @GetMapping(value = REPORTS_URL + "/download") public void downloadExcel( HttpServletRequest request, HttpServletResponse response, @@ -292,15 +338,18 @@ public void downloadExcel( final User user = restControllerHelper.getUserByEmail(request.getParameter("user")); try { ByteBuffer buffer = null; + String fileName = sw360ReportService.getDocumentName(user, null, module); switch (module) { case SW360Constants.PROJECTS: buffer = sw360ReportService.getReportStreamFromURl(user, extendedByReleases, token); + fileName = sw360ReportService.getDocumentName(user, request.getParameter("projectId"), module); break; case SW360Constants.COMPONENTS: buffer = sw360ReportService.getComponentReportStreamFromURl(user, extendedByReleases, token); break; case SW360Constants.LICENSES: buffer = sw360ReportService.getLicenseReportStreamFromURl(token); + fileName = String.format("licenses-%s.xlsx", SW360Utils.getCreatedOn()); break; default: break; @@ -308,17 +357,7 @@ public void downloadExcel( if (null == buffer) { throw new TException("No data available for the user " + user.getEmail()); } - String fileName; - if(module.equals(SW360Constants.LICENSES)) { - fileName = String.format("licenses-%s.xlsx", SW360Utils.getCreatedOn()); - } else if(module.equals(SW360Constants.PROJECTS) || module.equals(SW360Constants.PROJECT_RELEASE_SPREADSHEET_WITH_ECCINFO)) { - fileName = sw360ReportService.getDocumentName(user, request.getParameter("projectId"), module); - } else if(module.equals(LICENSE_INFO)) { - fileName = sw360ReportService.getGenericLicInfoFileName(request, user); - } else { - fileName = sw360ReportService.getDocumentName(user, null, module); - } - response.setContentType(SW360Constants.CONTENT_TYPE); + response.setContentType(CONTENT_TYPE_OPENXML_SPREADSHEET); response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName)); copyDataStreamToResponse(response, buffer); } catch (Exception e) { @@ -332,13 +371,4 @@ private String getBaseUrl(HttpServletRequest request) { String ctx = request.getContextPath(); return url.substring(0, url.length() - uri.length() + ctx.length()) + "/"; } - - private void getProjectReleaseWithEccSpreadSheet(HttpServletResponse response, User sw360User, String module, - String projectId, HttpServletRequest request, boolean excludeReleaseVersion) throws TException { - try { - downloadExcelReport(false, request, response, sw360User, module, projectId, excludeReleaseVersion, defaultByteBufferVal); - } catch (Exception e) { - throw new TException(e.getMessage()); - } - } } 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 db6980afb5..7cfe136317 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 @@ -9,18 +9,22 @@ import java.io.IOException; import java.net.URL; import java.nio.ByteBuffer; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -import jakarta.servlet.http.HttpServletRequest; - import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; 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.ProjectReleaseRelationship; import org.eclipse.sw360.datahandler.thrift.ThriftClients; +import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType; +import org.eclipse.sw360.datahandler.thrift.attachments.SourcePackageUsage; import org.eclipse.sw360.datahandler.thrift.components.ComponentService; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.components.ReleaseClearingStatusData; @@ -37,21 +41,17 @@ import static org.eclipse.sw360.rest.resourceserver.Sw360ResourceServer.REPORT_FILENAME_MAPPING; 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 jakarta.servlet.http.HttpServletRequest; + import org.apache.commons.lang.StringUtils; -import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.thrift.Source; 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.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; @@ -67,26 +67,18 @@ import com.google.common.base.Strings; import lombok.NonNull; -import java.io.IOException; + import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.Optional; import java.util.concurrent.TimeUnit; -import org.apache.commons.io.IOUtils; - import org.eclipse.sw360.datahandler.common.Duration; import org.eclipse.sw360.datahandler.couchdb.AttachmentStreamConnector; -import org.eclipse.sw360.datahandler.permissions.PermissionUtils; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentService; -import org.eclipse.sw360.datahandler.thrift.attachments.SourcePackageUsage; - -import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; @Service @@ -108,6 +100,7 @@ public class SW360ReportService { @NonNull private final Sw360LicenseInfoService licenseInfoService; + private static final Logger log = LogManager.getLogger(SW360ReportService.class); ThriftClients thriftClients = new ThriftClients(); ProjectService.Iface projectclient = thriftClients.makeProjectClient(); ComponentService.Iface componentclient = thriftClients.makeComponentClient(); @@ -307,10 +300,8 @@ private void getSelectedAttchIdsAndExcludedLicInfo(User sw360User, List getExcludedLicenses(Set excludedLicense return licenseInfoParsingResult.stream().map(LicenseInfoParsingResult::getLicenseInfo) .flatMap(streamLicenseNameWithTexts).filter(filteredLicense).collect(Collectors.toSet()); } - + public ByteBuffer getLicenseResourceBundleBuffer() throws TException { return licenseClient.getLicenseReportDataStream(); } - public ByteBuffer downloadSourceCodeBundle(String projectId, HttpServletRequest request, User sw360User) + public ByteBuffer downloadSourceCodeBundle(String projectId, User sw360User, boolean withSubProject) throws IOException, TException { if (projectId == null || !validateProject(projectId, sw360User)) { throw new TException("No project record found for the project Id : " + projectId); } - Map> selectedReleaseAndAttachmentIds = getSelectedReleaseAndAttachmentIdsFromRequest( - request, false); - Set selectedAttachmentIds = new HashSet<>(); - selectedReleaseAndAttachmentIds.forEach((key, value) -> selectedAttachmentIds.addAll(value)); Project project = projectclient.getProjectById(projectId, sw360User); - saveSourcePackageAttachmentUsages(project, sw360User, selectedReleaseAndAttachmentIds); List attachments = new ArrayList<>(); - for (String id : selectedAttachmentIds) { + for (String id : getAttachmentIdFromAttachmentUsages(project, sw360User, withSubProject)) { attachments.add(attachmentClient.getAttachmentContent(id)); } - return serveAttachmentBundle(attachments, request, project, sw360User); - } - - public static Map> getSelectedReleaseAndAttachmentIdsFromRequest(HttpServletRequest request, - boolean withPath) { - Map> releaseIdToAttachmentIds = new HashMap<>(); - String[] checkboxes = request.getParameterValues("licenseInfoAttachmentSelected"); - if (checkboxes == null) { - return ImmutableMap.of(); - } - Arrays.stream(checkboxes).forEach(s -> { - String[] split = s.split(":"); - if (split.length >= 2) { - String attachmentId = split[split.length - 1]; - String releaseIdMaybeWithPath; - if (withPath) { - releaseIdMaybeWithPath = Arrays.stream(Arrays.copyOf(split, split.length - 1)) - .collect(Collectors.joining(":")); - } else { - releaseIdMaybeWithPath = split[split.length - 2]; - } - releaseIdToAttachmentIds.putIfAbsent(releaseIdMaybeWithPath, new HashSet<>()).add(attachmentId); - } - }); - return releaseIdToAttachmentIds; - } - - private void saveSourcePackageAttachmentUsages(Project project, User user, - Map> selectedReleaseAndAttachmentIds) throws TException { - Function usageDataGenerator = attachmentContentId -> UsageData - .sourcePackage(new SourcePackageUsage()); - List attachmentUsages = makeAttachmentUsages(project, selectedReleaseAndAttachmentIds, - usageDataGenerator); - replaceAttachmentUsages(project, user, attachmentUsages, UsageData.sourcePackage(new SourcePackageUsage())); + return serveAttachmentBundle(attachments, project, sw360User); } public String getSourceCodeBundleName(String projectId, User sw360User) throws TException { @@ -392,25 +345,20 @@ public String getSourceCodeBundleName(String projectId, User sw360User) throws T return "SourceCodeBundle-" + project.getName() + "-" + timestamp + ".zip"; } - private ByteBuffer serveAttachmentBundle(List attachments, HttpServletRequest request, - Project project, User sw360User) throws IOException, TException { + private ByteBuffer serveAttachmentBundle(List attachments, + Project project, User sw360User) throws IOException, TException { final Duration timeout = Duration.durationOf(30, TimeUnit.SECONDS); final AttachmentStreamConnector attachmentStreamConnector = new AttachmentStreamConnector(timeout); - return getAttachmentBundleByteBuffer(attachmentStreamConnector, attachments, request, project, sw360User); + return getAttachmentBundleByteBuffer(attachmentStreamConnector, attachments, project, sw360User); } private ByteBuffer getAttachmentBundleByteBuffer(AttachmentStreamConnector attachmentStreamConnector, - List attachments, HttpServletRequest request, Project project, User sw360User) + List attachments, Project project, User sw360User) throws TException, IOException { - String isAllAttachment = request.getParameter("isAllAttachmentSelected"); InputStream stream = null; Optional context = getContextFromRequest(project); if (context.isPresent()) { - if (StringUtils.isNotEmpty(isAllAttachment) && isAllAttachment.equalsIgnoreCase("true")) { - stream = getStreamToServeBundle(attachmentStreamConnector, attachments, sw360User, context); - } else { - stream = getStreamToServeAFile(attachmentStreamConnector, attachments, sw360User, context); - } + stream = getStreamToServeAFile(attachmentStreamConnector, attachments, sw360User, context); } return ByteBuffer.wrap(IOUtils.toByteArray(stream)); } @@ -419,43 +367,42 @@ private Optional getContextFromRequest(Project project) { return Optional.ofNullable(project); } - private void replaceAttachmentUsages(Project project, User user, List attachmentUsages, - UsageData defaultEmptyUsageData) throws TException { - if (PermissionUtils.makePermission(project, user).isActionAllowed(RequestedAction.WRITE)) { - AttachmentService.Iface attachmentClient = thriftClients.makeAttachmentClient(); - if (attachmentUsages.isEmpty()) { - attachmentClient.deleteAttachmentUsagesByUsageDataType(Source.projectId(project.getId()), - defaultEmptyUsageData); - } else { - attachmentClient.replaceAttachmentUsages(Source.projectId(project.getId()), attachmentUsages); - } - } else { - throw new TException( - "LicenseInfo usage is not stored since the user has no write permissions for this project."); + public List getAttachmentIdFromAttachmentUsages(Project sw360Project, User sw360User, boolean withSubProject) { + final Set attachmentIds = new HashSet<>(); + final Set projects = new HashSet<>(List.of(sw360Project)); + if (withSubProject) { + final Collection linkedProjects = SW360Utils.getLinkedProjectsAsFlatList(sw360Project, true, thriftClients, log, sw360User); + projects.addAll(linkedProjects.stream().map(link -> wrapTException(() -> projectService.getProjectForUserById(link.getId(), sw360User))).toList()); } - } - - public static List makeAttachmentUsages(Project project, - Map> selectedReleaseAndAttachmentIds, Function usageDataGenerator) { - List attachmentUsages = Lists.newArrayList(); - for (String releaseId : selectedReleaseAndAttachmentIds.keySet()) { - for (String attachmentContentId : selectedReleaseAndAttachmentIds.get(releaseId)) { - AttachmentUsage usage = new AttachmentUsage(); - usage.setUsedBy(Source.projectId(project.getId())); - usage.setOwner(Source.releaseId(releaseId)); - usage.setAttachmentContentId(attachmentContentId); - UsageData usageData = usageDataGenerator.apply(attachmentContentId); - usage.setUsageData(usageData); - attachmentUsages.add(usage); + for (Project project : projects) { + try { + List attachmentSourceUsages = attachmentClient.getUsedAttachments(Source.projectId(project.getId()), + UsageData.sourcePackage(new SourcePackageUsage())); + List currentProjAttachments = attachmentSourceUsages.stream().map(AttachmentUsage::getAttachmentContentId).toList(); + if (! currentProjAttachments.isEmpty()) { + attachmentIds.addAll(currentProjAttachments); + continue; + } + Map releaseUsage = project.getReleaseIdToUsage(); + try { + List releases = componentclient.getFullReleasesById(releaseUsage.keySet(), sw360User); + releases.forEach(release -> { + Set attachments = release.getAttachments(); + if (attachments != null) { + attachments.forEach(attachment -> { + if (attachment.getAttachmentType() == AttachmentType.SOURCE || attachment.getAttachmentType() == AttachmentType.SOURCE_SELF) { + attachmentIds.add(attachment.getAttachmentContentId()); + } + }); + } + }); + } catch (TException ignored) { + } + } catch (TException ignored) { } } - return attachmentUsages; - } - private InputStream getStreamToServeBundle(AttachmentStreamConnector attachmentStreamConnector, - List attachments, User sw360User, Optional context) - throws IOException, TException { - return attachmentStreamConnector.getAttachmentBundleStream(new HashSet<>(attachments), sw360User, context); + return attachmentIds.stream().toList(); } private InputStream getStreamToServeAFile(AttachmentStreamConnector attachmentStreamConnector, @@ -489,4 +436,4 @@ public ByteBuffer getProjectReleaseSpreadSheetWithEcc(User user, String projectI } return ByteBuffer.wrap(IOUtils.toByteArray(exporter.makeExcelExport(releases))); } -} \ No newline at end of file +} diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vendor/VendorController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vendor/VendorController.java index 8172da2456..eae860e1a9 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vendor/VendorController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vendor/VendorController.java @@ -54,6 +54,7 @@ import java.util.List; import jakarta.servlet.http.HttpServletResponse; +import static org.eclipse.sw360.datahandler.common.SW360Constants.CONTENT_TYPE_OPENXML_SPREADSHEET; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; @BasePathAwareController @@ -256,7 +257,7 @@ private HalResource createHalVendor(Vendor sw360Vendor) { @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Vendor spreadsheet.", content = { - @Content(mediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + @Content(mediaType = CONTENT_TYPE_OPENXML_SPREADSHEET) }) }) @PreAuthorize("hasAuthority('WRITE')") diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ComponentSpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ComponentSpecTest.java index 384f3ea114..4a7f7c5e96 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ComponentSpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ComponentSpecTest.java @@ -1330,7 +1330,6 @@ public void should_document_get_component_report() throws Exception{ mockMvc.perform(get("/api/reports") .header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) .queryParam("withlinkedreleases", "true") - .queryParam("mimetype", "xlsx") .queryParam("module", "components") .queryParam("excludeReleaseVersion", "false") .accept(MediaTypes.HAL_JSON)) @@ -1338,7 +1337,6 @@ public void should_document_get_component_report() throws Exception{ .andDo(this.documentationHandler.document( queryParameters( parameterWithName("withlinkedreleases").description("Projects with linked releases. Possible values are ``"), - parameterWithName("mimetype").description("Projects download format. Possible values are ``"), parameterWithName("module").description("module represent the project or component. Possible values are ``"), parameterWithName("excludeReleaseVersion").description("Exclude version of the components from the generated license info file. " + "Possible values are ``") 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 10ed7366c0..c7910f8b3a 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 @@ -588,7 +588,7 @@ public void before() throws TException, IOException { 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(project8.getId()), any())).willReturn(project8); - given(this.sw360ReportServiceMock.downloadSourceCodeBundle(any(), any(), any())).willReturn(ByteBuffer.allocate(10000)); + given(this.sw360ReportServiceMock.downloadSourceCodeBundle(any(), any(), anyBoolean())).willReturn(ByteBuffer.allocate(10000)); given(this.sw360ReportServiceMock.getLicenseInfoBuffer(any(), any(), any(), any(), any(), any(), anyBoolean())).willReturn(ByteBuffer.allocate(10000)); given(this.sw360ReportServiceMock.getSourceCodeBundleName(any(), any())).willReturn("SourceCodeBundle-ProjectName"); given(this.projectServiceMock.getLicenseInfoAttachmentUsage(eq(project8.getId()))).willReturn(licenseInfoUsages); @@ -2166,7 +2166,7 @@ public void should_document_get_download_license_info() throws Exception { parameterWithName("externalIds").description("The external Ids of the project") ))); } - + @Test public void should_document_get_download_license_info_without_release_version() throws Exception { this.mockMvc.perform(get("/api/projects/" + project.getId()+ "/licenseinfo?generatorClassName=XhtmlGenerator&variant=DISCLOSURE&excludeReleaseVersion=true") @@ -2469,7 +2469,6 @@ public void should_document_get_project_report() throws Exception { mockMvc.perform(get("/api/reports"). header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) .queryParam("withlinkedreleases", "true") - .queryParam("mimetype", "xlsx") .queryParam("module", "projects") .queryParam("excludeReleaseVersion", "false") .accept(MediaTypes.HAL_JSON)) @@ -2477,7 +2476,6 @@ public void should_document_get_project_report() throws Exception { .andDo(this.documentationHandler.document( queryParameters( parameterWithName("withlinkedreleases").description("Projects with linked releases. Possible values are ``"), - parameterWithName("mimetype").description("Projects download format. Possible values are ``"), parameterWithName("module").description("module represent the project or component. Possible values are ``"), parameterWithName("excludeReleaseVersion").description("Exclude version of the components from the generated license info file. " + "Possible values are ``") @@ -2490,7 +2488,6 @@ public void should_document_get_project_licenseclearing_spreadsheet() throws Exc mockMvc.perform(get("/api/reports") .header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) .queryParam("withlinkedreleases", "true") - .queryParam("mimetype", "xlsx") .queryParam("module", "projects") .queryParam("projectId", project.getId()) .queryParam("excludeReleaseVersion", "false") @@ -2499,7 +2496,6 @@ public void should_document_get_project_licenseclearing_spreadsheet() throws Exc .andDo(this.documentationHandler.document( queryParameters( parameterWithName("withlinkedreleases").description("Projects with linked releases. Possible values are ``"), - parameterWithName("mimetype").description("Projects download format. Possible values are ``"), parameterWithName("module").description("module represent the project or component. Possible values are ``"), parameterWithName("projectId").description("Id of a project"), parameterWithName("excludeReleaseVersion").description("Exclude version of the components from the generated license info file. " @@ -3042,11 +3038,11 @@ public void should_document_get_linked_releases_in_dependency_network_by_index_p public void should_document_get_resource_source_bundle() throws Exception { mockMvc.perform(get("/api/reports").header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)) .param("module", "licenseResourceBundle").param("projectId", project.getId()) - .param("isAllAttachmentSelected", "true").accept(MediaTypes.HAL_JSON)).andExpect(status().isOk()) + .param("withSubProject", "true").accept(MediaTypes.HAL_JSON)).andExpect(status().isOk()) .andDo(this.documentationHandler.document(queryParameters( parameterWithName("projectId").description("Project id"), - parameterWithName("isAllAttachmentSelected").description( - "All attachment selected to download source code bundle. Possible values are ``"), + parameterWithName("withSubProject").description( + "Use subprojects as well to download source code bundle. Possible values are ``"), parameterWithName("module").description( "module represent the type oa document. Possible values are ``")))); } @@ -3204,4 +3200,4 @@ public void should_document_get_project_release_with_ecc_spreadsheet() throws Ex parameterWithName("projectId").description("Id of a project")) )); } -} \ No newline at end of file +}