Skip to content

Commit

Permalink
feat(REST):Create clearing request for a project
Browse files Browse the repository at this point in the history
Signed-off-by: afsahsyeda <[email protected]>
  • Loading branch information
afsahsyeda committed Jan 30, 2024
1 parent ecce96c commit 558a347
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ public AddDocumentRequestSummary createClearingRequest(ClearingRequest clearingR
clearingRequest.setId(crId);
updateProject(project, user);
sendMailForNewClearing(project, projectUrl, clearingRequest, user);
//set requestSummary message that creation of CR was successful
requestSummary.setMessage("Clearing request created successfully");
return requestSummary.setRequestStatus(AddDocumentRequestStatus.SUCCESS).setId(project.getClearingRequestId());
} else {
log.error("Failed to create clearing request for project: " + project.getId());
Expand Down
14 changes: 14 additions & 0 deletions rest/resource-server/src/docs/asciidoc/projects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -862,3 +862,17 @@ include::{snippets}/should_document_update_project_with_network/curl-request.ado

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

[[resources-clearing-request-create]]
==== Creating a clearing request

A `POST` request is used to create the clearing request for a project.

===== Request structure
include::{snippets}/should_document_create_clearing_request/request-fields.adoc[]

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

===== Example response
include::{snippets}/should_document_create_clearing_request/http-response.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,18 @@ public User getUserByEmail(String emailId) {
return sw360User;
}

public User getUserByEmailOrNull(String emailId) {
User sw360User;
try {
sw360User = userService.getUserByEmail(emailId);
} catch (RuntimeException e) {
LOGGER.debug("Could not get user object from backend with email: " + emailId);
return null;
}
return sw360User;
}


public void addEmbeddedContributors(HalResource halResource, Set<String> contributors) {
for (String contributorEmail : contributors) {
User sw360User = getUserByEmail(contributorEmail);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
import org.eclipse.sw360.datahandler.resourcelists.PaginationParameterException;
import org.eclipse.sw360.datahandler.resourcelists.PaginationResult;
import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException;
import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestStatus;
import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary;
import org.eclipse.sw360.datahandler.thrift.ClearingRequestState;
import org.eclipse.sw360.datahandler.thrift.ClearingRequestType;
import org.eclipse.sw360.datahandler.thrift.MainlineState;
import org.eclipse.sw360.datahandler.thrift.ProjectReleaseRelationship;
import org.eclipse.sw360.datahandler.thrift.ReleaseRelationship;
Expand All @@ -71,6 +75,7 @@
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.projects.ClearingRequest;
import org.eclipse.sw360.datahandler.thrift.projects.Project;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink;
Expand Down Expand Up @@ -126,6 +131,9 @@
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -1458,8 +1466,8 @@ private HalResource attachmentUsageReleases(Project sw360Project, User sw360User
Map<String, ProjectReleaseRelationship> releaseIdToUsages = sw360Project.getReleaseIdToUsage();
Map<String, Object> projectMap = oMapper.convertValue(sw360Project, Map.class);
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("linkedProjects", (Map<String, Object>) projectMap.get("linkedProjects"));
resultMap.put("releaseIdToUsage", (Map<String, Object>) projectMap.get("releaseIdToUsage"));
resultMap.put("linkedProjects", projectMap.get("linkedProjects"));
resultMap.put("releaseIdToUsage", projectMap.get("releaseIdToUsage"));

Map<String, Object> releaseIdToUsage = (Map<String, Object>) resultMap.get("releaseIdToUsage");
final ImmutableSet<String> fieldsToRemove = ImmutableSet.of("setCreatedBy", "setCreatedOn", "setComment", "setReleaseRelation", "setMainlineState");
Expand Down Expand Up @@ -1914,6 +1922,13 @@ private Project convertToProject(Map<String, Object> requestBody) {
return mapper.convertValue(requestBody, Project.class);
}

private ClearingRequest convertToClearingRequest(Map<String, Object> requestBody) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(sw360Module);
return mapper.convertValue(requestBody, ClearingRequest.class);
}

public static TSerializer getJsonSerializer() {
try {
return new TSerializer(new TSimpleJSONProtocol.Factory());
Expand Down Expand Up @@ -2176,4 +2191,87 @@ private void updateReleaseNodeData(ReleaseNode releaseNode, ProjectOperation ope
}
}
}

private ClearingRequest convertToClearingRequest(Map<String, Object> requestBody, String projectId) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ClearingRequest clearingRequest = mapper.convertValue(requestBody, ClearingRequest.class);
return clearingRequest;
}

/**
* Creates a clearing request for a project.
*
* @param id The project ID.
* @param reqBodyMap The clearing request
* @return The response entity containing the result of the operation.
* @throws TException If an error occurs during the operation.
*/
@PreAuthorize("hasAuthority('WRITE')")
@Operation(
summary = "Create a clearing request for a project.",
tags = {"Projects"}
)
@RequestMapping(value = PROJECTS_URL + "/{id}/clearingRequest", method = RequestMethod.POST)
public ResponseEntity<?> createClearingRequest(
@Parameter(description = "Project ID", example = "376576")
@PathVariable("id") String id,
@Parameter(description = "Clearing request",
schema = @Schema(implementation = ClearingRequest.class))
@RequestBody Map<String, Object> reqBodyMap
) throws TException {
User sw360User = restControllerHelper.getSw360UserFromAuthentication();
Project sw360Project = projectService.getProjectForUserById(id, sw360User);
ClearingRequest clearingRequest = convertToClearingRequest(reqBodyMap, id);
clearingRequest.setProjectId(id);
clearingRequest.setRequestingUser(sw360User.getEmail());
clearingRequest.setClearingState(ClearingRequestState.NEW);

if (clearingRequest.getRequestingUserComment() == null) {
clearingRequest.setRequestingUserComment("");
}
if (clearingRequest.getClearingType() == null) {
return new ResponseEntity<String>("clearingType is a mandatory field. Possible values are:"
+ Arrays.asList(ClearingRequestType.values()), HttpStatus.BAD_REQUEST);
}
//Generated by AI(GithubCopilot)
if (clearingRequest.getRequestedClearingDate() != null) {
try {
LocalDate.parse(clearingRequest.getRequestedClearingDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDate today = LocalDate.now();
LocalDate requestedClearingDate = LocalDate.parse(clearingRequest.getRequestedClearingDate(),
DateTimeFormatter.ofPattern("yyyy-MM-dd"));
if (requestedClearingDate.isBefore(today)) {
return new ResponseEntity<String>("requestedClearingDate must be a current or future date",
HttpStatus.BAD_REQUEST);
}
} catch (DateTimeParseException e) {
return new ResponseEntity<String>("requestedClearingDate must be in yyyy-MM-dd format",
HttpStatus.BAD_REQUEST);
}
}
if (clearingRequest.getClearingTeam() != null) {
User user = restControllerHelper.getUserByEmailOrNull(clearingRequest.getClearingTeam());
if (user == null) {
return new ResponseEntity<String>("clearingTeam is not a valid user", HttpStatus.BAD_REQUEST);
}
}

AddDocumentRequestSummary addDocumentRequestSummary = projectService.createClearingRequest(clearingRequest, sw360User);

if (addDocumentRequestSummary.getRequestStatus() == AddDocumentRequestStatus.DUPLICATE) {
return new ResponseEntity<String>(addDocumentRequestSummary.getMessage(), HttpStatus.CONFLICT);
} else if (addDocumentRequestSummary.getRequestStatus() == AddDocumentRequestStatus.FAILURE) {
return new ResponseEntity<String>(addDocumentRequestSummary.getMessage(), HttpStatus.BAD_REQUEST);
}
clearingRequest.setId(addDocumentRequestSummary.getId());

URI location = ServletUriComponentsBuilder
.fromCurrentRequest().path("/{id}")
.buildAndExpand(clearingRequest.getId()).toUri();

HalResource<ClearingRequest> halResource = new HalResource<>(clearingRequest);

return ResponseEntity.created(location).body(halResource);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.sw360.datahandler.common.SW360Utils;
import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestStatus;
import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary;
import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest;
import org.eclipse.sw360.datahandler.thrift.PaginationData;
import org.eclipse.sw360.datahandler.thrift.ProjectReleaseRelationship;
import org.eclipse.sw360.datahandler.thrift.ReleaseRelationship;
Expand Down Expand Up @@ -223,7 +224,7 @@ public void deleteAllProjects(User sw360User) throws TException {
}
}
}

public Project getClearingInfo(Project sw360Project, User sw360User) throws TException {
ProjectService.Iface sw360ProjectClient = getThriftProjectClient();
return sw360ProjectClient.fillClearingStateSummaryIncludingSubprojectsForSingleProject(sw360Project, sw360User);
Expand Down Expand Up @@ -566,4 +567,10 @@ public int countProjectsByReleaseIds(Set<String> releaseIds) {
return 0;
}
}

public AddDocumentRequestSummary createClearingRequest(ClearingRequest clearingRequest, User sw360User) throws TException {
ProjectService.Iface sw360ProjectClient = getThriftProjectClient();
return sw360ProjectClient.createClearingRequest(clearingRequest, sw360User, " ");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@
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.ClearingRequestState;
import org.eclipse.sw360.datahandler.thrift.ClearingRequestType;
import org.eclipse.sw360.datahandler.thrift.CycloneDxComponentType;
import org.eclipse.sw360.datahandler.thrift.MainlineState;
import org.eclipse.sw360.datahandler.thrift.ProjectReleaseRelationship;
import org.eclipse.sw360.datahandler.thrift.ReleaseRelationship;
import org.eclipse.sw360.datahandler.thrift.RequestStatus;
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestStatus;
import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary;
import org.eclipse.sw360.datahandler.thrift.Source;
import org.eclipse.sw360.datahandler.thrift.Visibility;
import org.eclipse.sw360.datahandler.thrift.attachments.Attachment;
Expand All @@ -41,6 +45,7 @@
import org.eclipse.sw360.datahandler.thrift.licenseinfo.OutputFormatVariant;
import org.eclipse.sw360.datahandler.thrift.packages.Package;
import org.eclipse.sw360.datahandler.thrift.packages.PackageManager;
import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest;
import org.eclipse.sw360.datahandler.thrift.projects.Project;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState;
import org.eclipse.sw360.datahandler.thrift.projects.ProjectProjectRelationship;
Expand Down Expand Up @@ -446,6 +451,12 @@ public void before() throws TException, IOException {
RequestSummary requestSummaryForCycloneDX = new RequestSummary();
requestSummaryForCycloneDX.setMessage("{\"projectId\":\"" + cycloneDXProject.getId() + "\"}");

AddDocumentRequestSummary requestSummaryForCR = new AddDocumentRequestSummary();
requestSummaryForCR.setMessage("Clearing request created successfully");
requestSummaryForCR.setRequestStatus(AddDocumentRequestStatus.SUCCESS);
requestSummaryForCR.setId("CR-1");

given(this.projectServiceMock.createClearingRequest(any(),any())).willReturn(requestSummaryForCR);
given(this.projectServiceMock.importSPDX(any(),any())).willReturn(requestSummaryForSPDX);
given(this.projectServiceMock.importCycloneDX(any(),any(),any())).willReturn(requestSummaryForCycloneDX);
given(this.sw360ReportServiceMock.getProjectBuffer(any(),anyBoolean())).willReturn(ByteBuffer.allocate(10000));
Expand Down Expand Up @@ -583,6 +594,8 @@ public void before() throws TException, IOException {
new User("[email protected]", "sw360").setId("123456789"));
given(this.userServiceMock.getUserByEmail("[email protected]")).willReturn(
new User("[email protected]", "sw360").setId("209582812"));
given(this.userServiceMock.getUserByEmail("[email protected]")).willReturn(
new User("[email protected]", "sw360").setId("2012312"));
OutputFormatInfo outputFormatInfo = new OutputFormatInfo();
outputFormatInfo.setFileExtension("html");
given(this.licenseInfoMockService.getOutputFormatInfoForGeneratorClass(any()))
Expand Down Expand Up @@ -1467,6 +1480,41 @@ public void should_document_get_project_attachment() throws Exception {
.andDo(this.documentationHandler.document());
}

@Test
public void should_document_create_clearing_request() throws Exception {
Map<String, Object> cr = new HashMap<>();
cr.put("clearingType", ClearingRequestType.HIGH.toString());
cr.put("clearingTeam", "[email protected]");
cr.put("requestedClearingDate", "2024-02-24");
cr.put("requestingUserComment", "New clearing");

String accessToken = TestHelper.getAccessToken(mockMvc, testUserId, testUserPassword);
this.mockMvc.perform(post("/api/projects/" + project.getId() + "/clearingRequest")
.contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(cr))
.header("Authorization", "Bearer " + accessToken))
.andExpect(status().isCreated())
.andDo(this.documentationHandler.document(
links(linkWithRel("self").description("The <<resources-projects,Projects resource>>")),
requestFields(fieldWithPath("clearingTeam").description("Email of the clearing team."),
fieldWithPath("requestedClearingDate").description(
"Requested clearing date of the project. It should be a current or future date in the format yyyy-MM-dd."),
fieldWithPath("clearingType").description("Clearing type of the project. Possible values are: " + Arrays.asList(ClearingRequestType.values()).toString()),
fieldWithPath("requestingUserComment").description("Requesting user comment on the clearing of the project.")
),
responseFields(fieldWithPath("id").description("Clearing request id."),
fieldWithPath("projectId").description("Project id associated with clearing request."),
fieldWithPath("clearingState").description("Clearing state of the project."),
fieldWithPath("clearingTeam").description("Clearing team of the project."),
fieldWithPath("requestedClearingDate")
.description("Requested clearing date of the project."),
fieldWithPath("clearingType").description("Clearing type of the project."),
fieldWithPath("requestingUser").description("User requesting the clearing of the project."),
fieldWithPath("requestingUserComment").description("Requesting user comment on the clearing of the project."),
subsectionWithPath("_links").description("<<resources-index-links,Links>> to other resources")
)));
}

@Test
public void should_document_create_project() throws Exception {
Map<String, Object> project = new HashMap<>();
Expand Down

0 comments on commit 558a347

Please sign in to comment.