Skip to content

Commit

Permalink
Merge pull request eclipse-sw360#1968 from toshiba/release/feature-ap…
Browse files Browse the repository at this point in the history
…i_import_sbom_for_component

feat(api): create new endpoint import bom for component

Reviewed by: [email protected]
Tested by: [email protected]
  • Loading branch information
ag4ums authored May 30, 2023
2 parents d301e46 + e6374e8 commit 6f16ffe
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ private RequestSummary importSpdxBOM(InputStream inputStream, AttachmentContent
final RequestSummary requestSummary = new RequestSummary();
List<SpdxElement> describedPackages = new ArrayList<>();
String fileType = getFileType(attachmentContent.getFilename());
if (!"rdf".equals(fileType) && !"spdx".equals(fileType)) {
requestSummary.setRequestStatus(RequestStatus.FAILURE);
return requestSummary;
}
final String ext = "." + fileType;

final File sourceFile = DatabaseHandlerUtil.saveAsTempFile( inputStream, attachmentContent.getId(), ext);
Expand Down
66 changes: 66 additions & 0 deletions rest/resource-server/src/docs/asciidoc/components.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -425,3 +425,69 @@ include::{snippets}/should_document_update_component_vulnerabilities/http-respon

===== Links
include::{snippets}/should_document_update_component_vulnerabilities/links.adoc[]

[[resources-prepare-import-sbom-components]]
==== Prepare Import SBOM Component

A `POST` request is used to import SBOM in SPDX format.

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

|type
|File type of SBOM
|===

[red]#Request structure#
|===
Type |Description

|file
|Path of the SBOM file
|===


[red]#Response structure#
|===
|Complete Component will be returned
|===

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

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

[[resources-import-sbom-components]]
==== Import SBOM Component

A `POST` request is used to import SBOM in SPDX format.

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

|type
|File type of SBOM
|===

[red]#Request structure#
|===
Type |Description

|file
|Path of the SBOM file
|===


[red]#Response structure#
|===
|Complete Component will be returned
|===

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

===== Example response
include::{snippets}/should_document_import_sbom_for_component/http-response.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import org.eclipse.sw360.datahandler.resourcelists.PaginationResult;
import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException;
import org.eclipse.sw360.datahandler.thrift.RequestStatus;
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
import org.eclipse.sw360.datahandler.thrift.ImportBomRequestPreparation;
import org.eclipse.sw360.datahandler.common.CommonUtils;
import org.eclipse.sw360.datahandler.thrift.Source;
import org.eclipse.sw360.datahandler.thrift.VerificationStateInfo;
import org.eclipse.sw360.datahandler.thrift.attachments.Attachment;
Expand Down Expand Up @@ -624,4 +627,72 @@ private boolean validateReleaseVulnerabilityRelationDTO(Map<String,Set<String>>
long countExternalIdsFromRequest = vulnerabilityState.getReleaseVulnerabilityRelationDTOs().stream().count();
return countExternalIdsActual != countExternalIdsFromRequest;
}

@PreAuthorize("hasAuthority('WRITE')")
@RequestMapping(value = COMPONENTS_URL + "/import/SBOM", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<?> importSBOM(@RequestParam(value = "type", required = true) String type,
@RequestBody MultipartFile file) throws TException {
final User sw360User = restControllerHelper.getSw360UserFromAuthentication();
Attachment attachment;
final RequestSummary requestSummary;
if(!type.equalsIgnoreCase("SPDX")) {
throw new IllegalArgumentException("SBOM file type is not valid. It currently only supports SPDX(.rdf/.spdx) files.");
}
try {
attachment = attachmentService.uploadAttachment(file, new Attachment(), sw360User);
try {
requestSummary = componentService.importSBOM(sw360User, attachment.getAttachmentContentId());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
} catch (IOException e) {
log.error("failed to upload attachment", e);
throw new RuntimeException("failed to upload attachment", e);
}
String releaseId = requestSummary.getMessage();
if (!(requestSummary.getRequestStatus() == RequestStatus.SUCCESS && CommonUtils.isNotNullEmptyOrWhitespace(releaseId))) {
return new ResponseEntity<>("Invalid SBOM file", HttpStatus.BAD_REQUEST);
}
Release release = componentService.getReleaseById(requestSummary.getMessage(),sw360User);
Component component = componentService.getComponentForUserById(release.getComponentId(),sw360User);
HttpStatus status = HttpStatus.OK;
HalResource<Component> halResource = createHalComponent(component, sw360User);
return new ResponseEntity<>(halResource, status);
}

@RequestMapping(value = COMPONENTS_URL + "/prepareImport/SBOM", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<?> prepareImportSBOM(@RequestParam(value = "type", required = true) String type,
@RequestBody MultipartFile file) throws TException {
final User sw360User = restControllerHelper.getSw360UserFromAuthentication();
Attachment attachment;
final ImportBomRequestPreparation importBomRequestPreparation;
if(!type.equalsIgnoreCase("SPDX")) {
throw new IllegalArgumentException("SBOM file type is not valid. It currently only supports SPDX(.rdf/.spdx) files.");
}
try {
attachment = attachmentService.uploadAttachment(file, new Attachment(), sw360User);
try {
importBomRequestPreparation = componentService.prepareImportSBOM(sw360User, attachment.getAttachmentContentId());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
} catch (IOException e) {
log.error("failed to upload attachment", e);
throw new RuntimeException("failed to upload attachment", e);
}
ImportBomRequestPreparation importBomRequestPreparationResponse = handleImportBomRequestPreparation(importBomRequestPreparation);
HttpStatus status = importBomRequestPreparationResponse != null ? HttpStatus.OK : HttpStatus.BAD_REQUEST;
return new ResponseEntity<>(importBomRequestPreparationResponse, status);
}

private ImportBomRequestPreparation handleImportBomRequestPreparation(ImportBomRequestPreparation importBomRequestPreparation) {
ImportBomRequestPreparation importBomRequestPreparationResponse = new ImportBomRequestPreparation();
if (importBomRequestPreparation.isComponentDuplicate && importBomRequestPreparation.isReleaseDuplicate) {
importBomRequestPreparationResponse.setMessage("The Component and Release existed !");
} else {
importBomRequestPreparationResponse.setComponentsName(importBomRequestPreparation.getComponentsName());
importBomRequestPreparationResponse.setReleasesName(importBomRequestPreparation.getReleasesName());
}
return importBomRequestPreparationResponse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,14 @@ public List<String> getReleaseIdsFromComponentId(String componentId, User user)
ComponentService.Iface sw360ComponentClient = getThriftComponentClient();
return sw360ComponentClient.getReleaseIdsFromComponentId(componentId, user);
}

public RequestSummary importSBOM(User user, String attachmentContentId) throws TException {
ComponentService.Iface sw360ComponentClient = getThriftComponentClient();
return sw360ComponentClient.importBomFromAttachmentContent(user, attachmentContentId);
}

public ImportBomRequestPreparation prepareImportSBOM(User user, String attachmentContentId) throws TException {
ComponentService.Iface sw360ComponentClient = getThriftComponentClient();
return sw360ComponentClient.prepareImportBom(user, attachmentContentId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.eclipse.sw360.datahandler.thrift.Comment;
import org.eclipse.sw360.datahandler.thrift.ProjectReleaseRelationship;
import org.eclipse.sw360.datahandler.thrift.VerificationStateInfo;
import org.eclipse.sw360.datahandler.thrift.Visibility;
import org.eclipse.sw360.datahandler.thrift.*;
import org.eclipse.sw360.datahandler.thrift.attachments.Attachment;
import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs;
import org.eclipse.sw360.datahandler.thrift.changelogs.ChangedFields;
Expand Down Expand Up @@ -99,6 +96,7 @@ public Sw360Module() {
setMixInAnnotation(ProjectProjectRelationship.class, Sw360Module.ProjectProjectRelationshipMixin.class);
setMixInAnnotation(ModerationRequest.class, Sw360Module.ModerationRequestMixin.class);
setMixInAnnotation(EmbeddedModerationRequest.class, Sw360Module.EmbeddedModerationRequestMixin.class);
setMixInAnnotation(ImportBomRequestPreparation.class, Sw360Module.ImportBomRequestPreparationMixin.class);
}

@JsonInclude(JsonInclude.Include.NON_NULL)
Expand Down Expand Up @@ -1355,5 +1353,22 @@ static abstract class EmbeddedModerationRequestMixin extends ModerationRequestMi
@JsonIgnore
abstract public boolean isRequestDocumentDelete();
}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties({
"requestStatus",
"isComponentDuplicate",
"isReleaseDuplicate",
"version",
"setVersion",
"setIsComponentDuplicate",
"setIsReleaseDuplicate",
"setComponentsName",
"setReleasesName",
"setMessage",
"setRequestStatus"
})
public static abstract class ImportBomRequestPreparationMixin extends ImportBomRequestPreparation {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
import com.google.common.collect.ImmutableSet;
import org.apache.thrift.TException;
import org.eclipse.sw360.datahandler.thrift.*;
import org.eclipse.sw360.datahandler.thrift.ImportBomRequestPreparation;
import org.eclipse.sw360.datahandler.thrift.RequestStatus;
import org.eclipse.sw360.datahandler.thrift.Visibility;
import org.eclipse.sw360.datahandler.thrift.VerificationState;
import org.eclipse.sw360.datahandler.thrift.VerificationStateInfo;
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
import org.eclipse.sw360.datahandler.thrift.attachments.Attachment;
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent;
import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType;
Expand Down Expand Up @@ -41,8 +47,11 @@
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.CollectionModel;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import java.io.IOException;
import java.text.SimpleDateFormat;
Expand Down Expand Up @@ -90,6 +99,10 @@ public class ComponentSpecTest extends TestRestDocsSpecBase {

private Project project;

private Component sBOMComponent;
private Attachment sBOMAttachment;
private RequestSummary requestSummary = new RequestSummary();

@Before
public void before() throws TException, IOException {
Set<Attachment> attachmentList = new HashSet<>();
Expand Down Expand Up @@ -354,6 +367,46 @@ public void before() throws TException, IOException {
given(this.vulnerabilityServiceMock.updateReleaseVulnerabilityRelation(any(), any())).willReturn(RequestStatus.SUCCESS);
given(this.vulnerabilityServiceMock.getVulnerabilitiesByReleaseId(any(), any())).willReturn(vulDtos);
angularComponent.setReleases(releaseList);

sBOMAttachment = new Attachment("3331231254", "bom.spdx.rdf");
sBOMAttachment.setSha1("df90312312312534543544375345345383");
sBOMAttachment.setAttachmentType(AttachmentType.SBOM);
Set<Attachment> attachments = new HashSet<>();
attachments.add(sBOMAttachment);

sBOMComponent = new Component();
sBOMComponent.setId("2222222");
sBOMComponent.setName("Maven");
sBOMComponent.setCreatedOn("2023-04-30");
sBOMComponent.setBusinessUnit("sw360 BA");
sBOMComponent.setComponentType(ComponentType.SERVICE);
sBOMComponent.setCreatedBy("[email protected]");
sBOMComponent.setAttachments(attachments);

Release release1 = new Release();
release1.setId("3333333");
release1.setComponentId("2222222");
release1.setName("Green Web");
release1.setVersion("1.0.0");
release1.setCreatedOn("2023-04-30");
release1.setComponentType(ComponentType.SERVICE);
release1.setCreatedBy("[email protected]");

requestSummary.setMessage(sBOMComponent.getId());
requestSummary.setRequestStatus(RequestStatus.SUCCESS);

ImportBomRequestPreparation importBomRequestPreparation = new ImportBomRequestPreparation();
importBomRequestPreparation.setComponentsName(sBOMComponent.getName());
StringBuilder relesaeName = new StringBuilder();
relesaeName.append(release1.getName());
relesaeName.append(" ");
relesaeName.append(release1.getVersion());
importBomRequestPreparation.setReleasesName(relesaeName.toString());

given(this.componentServiceMock.prepareImportSBOM(any(),any())).willReturn(importBomRequestPreparation);
given(this.componentServiceMock.importSBOM(any(),any())).willReturn(requestSummary);
given(this.componentServiceMock.getReleaseById(any(),any())).willReturn(release1);
given(this.componentServiceMock.getComponentForUserById(eq(sBOMComponent.getId()), any())).willReturn(sBOMComponent);
}

@Test
Expand Down Expand Up @@ -990,4 +1043,27 @@ public void should_document_get_releases_by_component() throws Exception {
)));
}

@Test
public void should_document_import_sbom_for_component() throws Exception {
MockMultipartFile file = new MockMultipartFile("file","file=@/bom.spdx.rdf".getBytes());
String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/api/components/import/SBOM")
.content(file.getBytes())
.contentType(MediaType.MULTIPART_FORM_DATA)
.header("Authorization", "Bearer " + accessToken)
.queryParam("type", "SPDX");
this.mockMvc.perform(builder).andExpect(status().isOk()).andDo(this.documentationHandler.document());
}

@Test
public void should_document_prepare_import_sbom_for_component() throws Exception {
MockMultipartFile file = new MockMultipartFile("file","file=@/bom.spdx.rdf".getBytes());
String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/api/components/prepareImport/SBOM")
.content(file.getBytes())
.contentType(MediaType.MULTIPART_FORM_DATA)
.header("Authorization", "Bearer " + accessToken)
.queryParam("type", "SPDX");
this.mockMvc.perform(builder).andExpect(status().isOk()).andDo(this.documentationHandler.document());
}
}

0 comments on commit 6f16ffe

Please sign in to comment.