Skip to content

Commit

Permalink
feat(rest): create new enpoint to upload component csv file.
Browse files Browse the repository at this point in the history
Signed-off-by: Nikesh kumar <kumar.nikesh@siemens.com>
  • Loading branch information
nikkuma7 authored and GMishx committed Jan 6, 2025
1 parent 5365f10 commit be7606f
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 13 deletions.
1 change: 1 addition & 0 deletions rest/resource-server/src/docs/asciidoc/api-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,4 @@ include::ecc.adoc[]
include::importExport.adoc[]
include::attachmentCleanUp.adoc[]
include::databaseSanitation.adoc[]
include::importExport.adoc[]
58 changes: 56 additions & 2 deletions rest/resource-server/src/docs/asciidoc/importExport.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//
// Copyright Siemens AG, 2024. Part of the SW360 Portal Project.
//
// This program and the accompanying materials are made
Expand Down Expand Up @@ -77,4 +76,59 @@ A `GET` request to download the component information in csv format.
include::{snippets}/should_document_get_download_component_details/curl-request.adoc[]

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

[[upload-component]]
==== Upload component csv file.

A `POST` request help to upload the component csv file.

[red]#Request parameter#
|===
|Parameter |Description
|componentFile |Upload the component CSV file.
|===

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

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

[[upload-release]]
==== Upload release link csv file.

A `POST` request help to upload the release csv file.

[red]#Request parameter#
|===
|Parameter |Description

|releaseFile
|Upload the release link csv file.
|===

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

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

[[upload-component-attachment]]
==== Upload component attachment file.

A `POST` request help to upload the component attachment file.

[red]#Request parameter#
|===
|Parameter |Description

|component
|Upload the component attachment file.
|===

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

===== Example response
include::{snippets}/should_document_upload_component_attachment_file/http-response.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@
import java.io.IOException;

import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.sw360.datahandler.common.CommonUtils;
import org.eclipse.sw360.datahandler.thrift.RequestStatus;
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
import org.eclipse.sw360.rest.resourceserver.project.ProjectController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
Expand All @@ -38,6 +43,13 @@
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.MediaType;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
Expand All @@ -47,11 +59,13 @@
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@RestController
@SecurityRequirement(name = "tokenAuth")
public class ImportExportController implements RepresentationModelProcessor<RepositoryLinksResource> {
public class ImportExportController implements RepresentationModelProcessor<RepositoryLinksResource> {

private static final Logger LOGGER = LoggerFactory.getLogger(ImportExportController.class);
public static final String IMPORTEXPORT_URL = "/importExport";

private static final MediaType form = null;

@NonNull
private final RestControllerHelper restControllerHelper;

Expand Down Expand Up @@ -114,7 +128,7 @@ public void downloadAttachmentSample(HttpServletResponse response) {
)
@PreAuthorize("hasAuthority('WRITE')")
@GetMapping(value = IMPORTEXPORT_URL + "/downloadAttachmentInfo")
public void downloadAttachmentInfo(HttpServletResponse response) {
public void downloadAttachmentInfo(HttpServletResponse response) throws TTransportException{
try {
User sw360User = restControllerHelper.getSw360UserFromAuthentication();
importExportService.getDownloadAttachmentInfo(sw360User, response);
Expand Down Expand Up @@ -198,4 +212,64 @@ public ResponseEntity<String> handleGlobalException(Exception e) {
LOGGER.error("Unhandled exception: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}

@Operation(
summary = "Upload component CSV file.",
description = "Upload a component CSV file to the system.",
tags = {"ImportExport"},
parameters = {
@Parameter(name = "Content-Type", in = ParameterIn.HEADER, required = true, description = "The content type of the request. Supported values: multipart/mixed or multipart/form-data.")
}
)
@RequestMapping(value = IMPORTEXPORT_URL + "/uploadComponent", method = RequestMethod.POST, consumes = {
MediaType.MULTIPART_MIXED_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<RequestSummary> uploadComponentCsv(
@Parameter(description = "The component csv file to be uploaded.") @RequestParam("componentFile") MultipartFile file,
HttpServletRequest request, HttpServletResponse response) throws TException, IOException, ServletException {

User sw360User = restControllerHelper.getSw360UserFromAuthentication();
RequestSummary requestSummary = importExportService.uploadComponent(sw360User, file, request, response);
return ResponseEntity.ok(requestSummary);
}

@Operation(
summary = "Release link file.",
description = "Release link file.",
tags = {"ImportExport"},
parameters = {
@Parameter(name = "Content-Type", in = ParameterIn.HEADER, required = true, description = "The content type of the request. Supported values: multipart/mixed or multipart/form-data."),
}
)
@RequestMapping(value = IMPORTEXPORT_URL + "/uploadRelease", method = RequestMethod.POST, consumes = {
MediaType.MULTIPART_MIXED_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<RequestSummary> uploadReleaseCsv(
@Parameter(description = "The release csv file to be uploaded.") @RequestParam("releaseFile") MultipartFile file,
HttpServletRequest request, HttpServletResponse response) throws TException, IOException, ServletException {

User sw360User = restControllerHelper.getSw360UserFromAuthentication();
RequestSummary requestSummary = importExportService.uploadReleaseLink(sw360User, file, request);
return ResponseEntity.ok(requestSummary);
}

@Operation(
summary = "Component attachment file.",
description = "Component attachment file.",
tags = {"ImportExport"},
parameters = {
@Parameter(name = "Content-Type", in = ParameterIn.HEADER, required = true, description = "The content type of the request. Supported values: multipart/mixed or multipart/form-data."),
}
)
@RequestMapping(value = IMPORTEXPORT_URL + "/componentAttachment", method = RequestMethod.POST, consumes = {
MediaType.MULTIPART_MIXED_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<RequestSummary> uploadComponentAttachment(
@Parameter(description = "The component attachment csv file to be uploaded.") @RequestParam("attachmentFile") MultipartFile file,
HttpServletRequest request, HttpServletResponse response) throws TException, IOException, ServletException {

User sw360User = restControllerHelper.getSw360UserFromAuthentication();
RequestSummary requestSummary = importExportService.uploadComponentAttachment(sw360User, file, request);
return ResponseEntity.ok(requestSummary);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,53 @@

import static org.eclipse.sw360.importer.ComponentImportUtils.getFlattenedView;
import static org.eclipse.sw360.importer.ComponentImportUtils.getReleasesById;
import static org.eclipse.sw360.datahandler.common.ImportCSV.readAsCSVRecords;
import static org.eclipse.sw360.importer.ComponentImportUtils.convertCSVRecordsToCompCSVRecords;
import static org.eclipse.sw360.importer.ComponentImportUtils.convertCSVRecordsToComponentAttachmentCSVRecords;
import static org.eclipse.sw360.importer.ComponentImportUtils.convertCSVRecordsToReleaseLinkCSVRecords;
import static org.eclipse.sw360.importer.ComponentImportUtils.writeAttachmentsToDatabase;
import static org.eclipse.sw360.importer.ComponentImportUtils.writeReleaseLinksToDatabase;
import static org.eclipse.sw360.importer.ComponentImportUtils.writeToDatabase;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransportException;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.RequestContext;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.eclipse.sw360.datahandler.common.SW360Utils;
import org.eclipse.sw360.datahandler.permissions.PermissionUtils;
import org.eclipse.sw360.datahandler.thrift.ReleaseRelationship;
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
import org.eclipse.sw360.datahandler.thrift.ThriftClients;
import org.eclipse.sw360.datahandler.thrift.ThriftUtils;
import org.eclipse.sw360.datahandler.thrift.attachments.Attachment;
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentService;
import org.eclipse.sw360.datahandler.thrift.components.Component;
import org.eclipse.sw360.datahandler.thrift.components.ComponentService;
import org.eclipse.sw360.datahandler.thrift.components.ComponentService.Iface;
import org.eclipse.sw360.datahandler.thrift.components.Release;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
import org.eclipse.sw360.datahandler.thrift.vendors.VendorService;
import org.eclipse.sw360.exporter.CSVExport;
import org.eclipse.sw360.importer.ComponentAttachmentCSVRecord;
import org.eclipse.sw360.importer.ComponentAttachmentCSVRecordBuilder;
Expand All @@ -44,20 +69,33 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;

import lombok.RequiredArgsConstructor;


@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class Sw360ImportExportService {
@Value("${sw360.thrift-server-url:http://localhost:8080}")
private String thriftServerUrl;
private final Logger log = LoggerFactory.getLogger(this.getClass());
private static final String CONTENT_DISPOSITION = "Content-Disposition";
ThriftClients thriftClients = new ThriftClients();

public void getDownloadCsvComponentTemplate(User sw360User, HttpServletResponse response) throws IOException {
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
Expand Down Expand Up @@ -86,7 +124,7 @@ public void getDownloadAttachmentTemplate(User sw360User, HttpServletResponse re
FileCopyUtils.copy(byteArrayInputStream, response.getOutputStream());
}

public void getDownloadAttachmentInfo(User sw360User, HttpServletResponse response) throws IOException {
public void getDownloadAttachmentInfo(User sw360User, HttpServletResponse response) throws IOException, TTransportException {
List<Iterable<String>> csvRows = new ArrayList<>();
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
throw new AccessDeniedException("User is not admin");
Expand Down Expand Up @@ -139,8 +177,8 @@ private void printAttachments(Set<Attachment> attachments, List<Iterable<String>
}
}

private List<Component> getComponentDetailedSummaryForExport() {
final ComponentService.Iface componentClient = getThriftComponentClient();
private List<Component> getComponentDetailedSummaryForExport() throws TTransportException{
final ComponentService.Iface componentClient = thriftClients.makeComponentClient();

final List<Component> componentDetailedSummaryForExport;
try {
Expand All @@ -153,10 +191,6 @@ private List<Component> getComponentDetailedSummaryForExport() {
return componentDetailedSummaryForExport;
}

private Iface getThriftComponentClient() {
return new ThriftClients().makeComponentClient();
}

public void getDownloadReleaseSample(User sw360User, HttpServletResponse response) throws TException, IOException {
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
throw new AccessDeniedException("User is not admin");
Expand Down Expand Up @@ -185,7 +219,8 @@ public void getDownloadReleaseLink(User sw360User, HttpServletResponse response)
}
}

ByteArrayInputStream byteArrayInputStream = CSVExport.createCSV(ReleaseLinkCSVRecord.getCSVHeaderIterable(), csvRows);
ByteArrayInputStream byteArrayInputStream = CSVExport.createCSV(ReleaseLinkCSVRecord.getCSVHeaderIterable(),
csvRows);
String filename = String.format("ReleaseLinkInfo_%s.csv", SW360Utils.getCreatedOn());
response.setHeader(CONTENT_DISPOSITION, String.format("Release; filename=\"%s\"", filename));
FileCopyUtils.copy(byteArrayInputStream, response.getOutputStream());
Expand Down Expand Up @@ -231,7 +266,8 @@ private void printReleaseLinkEntry(Component component, Release release, Release
csvRows.add(releaseLinkCSVRecordBuilder.build().getCSVIterable());
}

public void getComponentDetailedExport(User sw360User, HttpServletResponse response) throws TException, IOException {
public void getComponentDetailedExport(User sw360User, HttpServletResponse response)
throws TException, IOException {
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
throw new AccessDeniedException("User is not admin");
}
Expand All @@ -244,5 +280,67 @@ public void getComponentDetailedExport(User sw360User, HttpServletResponse respo
String filename = String.format("ComponentsReleasesVendors_%s.csv", SW360Utils.getCreatedOn());
response.setHeader(CONTENT_DISPOSITION, String.format("Components; filename=\"%s\"", filename));
FileCopyUtils.copy(byteArrayInputStream, response.getOutputStream());

}

@JsonInclude
public RequestSummary uploadComponent(User sw360User, MultipartFile file, HttpServletRequest request,
HttpServletResponse response) throws IOException, TException, ServletException {
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
throw new AccessDeniedException("Unable to upload component csv file. User is not admin");
}
List<CSVRecord> releaseRecords = getCSVFromRequest(request, "file");
FluentIterable<ComponentCSVRecord> compCSVRecords = convertCSVRecordsToCompCSVRecords(releaseRecords);
ComponentService.Iface sw360ComponentClient = thriftClients.makeComponentClient();
VendorService.Iface sw360VendorClient = thriftClients.makeVendorClient();
AttachmentService.Iface sw360AttachmentClient = thriftClients.makeAttachmentClient();
RequestSummary requestSummary = writeToDatabase(compCSVRecords, sw360ComponentClient, sw360VendorClient,
sw360AttachmentClient, sw360User);
return requestSummary;
}

private List<CSVRecord> getCSVFromRequest(HttpServletRequest request, String fileUploadFormId)
throws IOException, TException, ServletException {
final InputStream stream = getInputStreamFromRequest(request, fileUploadFormId);
return readAsCSVRecords(stream);
}

private InputStream getInputStreamFromRequest(HttpServletRequest request, String fileUploadFormId)
throws IOException, ServletException {
Collection<Part> parts = request.getParts();

for (Part part : parts) {
if (!part.getName().equals(fileUploadFormId)) {
return part.getInputStream();
}
}
throw new IOException("File not found in the request with the specified field name.");
}

public RequestSummary uploadReleaseLink(User sw360User, MultipartFile file, HttpServletRequest request)
throws IOException, TException, ServletException {
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
throw new AccessDeniedException("Unable to upload component csv file. User is not admin");
}
List<CSVRecord> releaseLinkRecords = getCSVFromRequest(request, "file");
FluentIterable<ReleaseLinkCSVRecord> csvRecords = convertCSVRecordsToReleaseLinkCSVRecords(releaseLinkRecords);
ComponentService.Iface sw360ComponentClient = thriftClients.makeComponentClient();
final RequestSummary requestSummary = writeReleaseLinksToDatabase(csvRecords, sw360ComponentClient, sw360User);
return requestSummary;
}

public RequestSummary uploadComponentAttachment(User sw360User, MultipartFile file, HttpServletRequest request)
throws IOException, TException, ServletException {
if (!PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
throw new AccessDeniedException("Unable to upload component attachment csv file. User is not admin");
}
List<CSVRecord> attachmentRecords = getCSVFromRequest(request, "file");
FluentIterable<ComponentAttachmentCSVRecord> compCSVRecords = convertCSVRecordsToComponentAttachmentCSVRecords(
attachmentRecords);
ComponentService.Iface sw360ComponentClient = thriftClients.makeComponentClient();
AttachmentService.Iface sw360AttachmentClient = thriftClients.makeAttachmentClient();
final RequestSummary requestSummary = writeAttachmentsToDatabase(compCSVRecords, sw360User,
sw360ComponentClient, sw360AttachmentClient);
return requestSummary;
}
}
Loading

0 comments on commit be7606f

Please sign in to comment.