Skip to content

Commit 3fff93d

Browse files
bibhuti230185GMishx
authored andcommitted
fix(component): accept comment in requestBody
Signed-off-by: Bibhuti Dash <[email protected]>, Gaurav Mishra <[email protected]>
1 parent 9d4afa6 commit 3fff93d

File tree

5 files changed

+73
-17
lines changed

5 files changed

+73
-17
lines changed

libraries/datahandler/src/main/thrift/components.thrift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ struct ComponentDTO {
407407
51: optional string mailinglist,
408408
52: optional string wiki,
409409
53: optional string blog,
410+
411+
// Moderation comment passed during PATCH request (not persisted on Component)
412+
90: optional string comment,
410413
}
411414

412415
struct ReleaseLink{

rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/component/ComponentController.java

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import org.springframework.data.domain.Pageable;
7373
import org.springframework.data.rest.webmvc.BasePathAwareController;
7474
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
75+
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
7576
import org.springframework.hateoas.*;
7677
import org.springframework.hateoas.server.RepresentationModelProcessor;
7778
import org.springframework.http.HttpStatus;
@@ -80,7 +81,6 @@
8081
import org.springframework.security.access.AccessDeniedException;
8182
import org.springframework.security.access.prepost.PreAuthorize;
8283
import org.springframework.web.bind.annotation.*;
83-
import org.springframework.web.client.HttpClientErrorException;
8484
import org.springframework.web.multipart.MultipartFile;
8585
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
8686

@@ -386,36 +386,85 @@ public ResponseEntity<CollectionModel<EntityModel<Component>>> searchByExternalI
386386
@PreAuthorize("hasAuthority('WRITE')")
387387
@Operation(
388388
summary = "Update an existing component.",
389-
description = "Update an existing component by its id.",
390-
tags = {"Components"}
389+
description = "Partially update an existing component. Only provided fields will be updated. " +
390+
"If the user lacks direct write access, a moderation request will be created. " +
391+
"Include a 'comment' field in the request body when submitting moderation requests.",
392+
tags = {"Components"},
393+
responses = {
394+
@ApiResponse(responseCode = "200", description = "Component updated successfully"),
395+
@ApiResponse(responseCode = "202", description = "Moderation request created"),
396+
@ApiResponse(responseCode = "400", description = "Invalid input or missing required fields"),
397+
@ApiResponse(responseCode = "401", description = "Unauthorized"),
398+
@ApiResponse(responseCode = "403", description = "Write access forbidden"),
399+
@ApiResponse(responseCode = "404", description = "Component not found")
400+
}
391401
)
392402
@RequestMapping(value = COMPONENTS_URL + "/{id}", method = RequestMethod.PATCH)
393403
public ResponseEntity<EntityModel<Component>> patchComponent(
394404
@Parameter(description = "The id of the component to be updated.")
395405
@PathVariable("id") String id,
396-
@Parameter(description = "The component with updated fields.")
397-
@RequestBody ComponentDTO updateComponentDto,
398-
@Parameter(description = "Comment message.")
399-
@RequestParam(value = "comment", required = false) String comment
406+
@Parameter(description = "Updated component fields. Add 'comment' field in body for moderation request.")
407+
@RequestBody ComponentDTO updateComponentDto
400408
) throws TException {
401-
User user = restControllerHelper.getSw360UserFromAuthentication();
402-
Component sw360Component = componentService.getComponentForUserById(id, user);
403-
user.setCommentMadeDuringModerationRequest(comment);
409+
final User user = restControllerHelper.getSw360UserFromAuthentication();
410+
restControllerHelper.throwIfSecurityUser(user);
411+
Component sw360Component = validateAndGetComponent(id, updateComponentDto, user);
412+
String comment = extractModerationComment(updateComponentDto);
404413
if (!restControllerHelper.isWriteActionAllowed(sw360Component, user)
405414
&& (comment == null || comment.isBlank())) {
406415
throw new BadRequestClientException(RESPONSE_BODY_FOR_MODERATION_REQUEST_WITH_COMMIT.toString());
407416
}
408-
if (updateComponentDto.getAttachments() != null) {
409-
updateComponentDto.getAttachments().forEach(attachment -> wrapSW360Exception(
410-
() -> this.attachmentService.fillCheckedAttachmentData(attachment, user)));
417+
user.setCommentMadeDuringModerationRequest(comment);
418+
419+
if (updateComponentDto.getAttachments() != null && !updateComponentDto.getAttachments().isEmpty()) {
420+
updateComponentDto.getAttachments().forEach(attachment ->
421+
wrapSW360Exception(() -> attachmentService.fillCheckedAttachmentData(attachment, user))
422+
);
411423
}
412-
sw360Component = this.restControllerHelper.updateComponent(sw360Component, updateComponentDto);
424+
425+
sw360Component = restControllerHelper.updateComponent(sw360Component, updateComponentDto);
413426
RequestStatus updateComponentStatus = componentService.updateComponent(sw360Component, user);
414-
HalResource<Component> userHalResource = createHalComponent(sw360Component, user);
427+
415428
if (updateComponentStatus == RequestStatus.SENT_TO_MODERATOR) {
416429
return new ResponseEntity(RESPONSE_BODY_FOR_MODERATION_REQUEST, HttpStatus.ACCEPTED);
417430
}
418-
return new ResponseEntity<>(userHalResource, HttpStatus.OK);
431+
432+
HalResource<Component> halResource = createHalComponent(sw360Component, user);
433+
return ResponseEntity.ok(halResource);
434+
}
435+
436+
private String extractModerationComment(ComponentDTO updateComponentDto) {
437+
try {
438+
String comment = updateComponentDto.getComment();
439+
return (comment != null && !comment.isBlank()) ? comment.trim() : null;
440+
} catch (Exception e) {
441+
log.debug("Comment field not available in ComponentDTO: {}", e.getMessage());
442+
return null;
443+
}
444+
}
445+
446+
447+
private Component validateAndGetComponent(String id, ComponentDTO updateComponentDto, User user) {
448+
if (isNullOrEmpty(id)) {
449+
throw new BadRequestClientException("Component ID cannot be null or empty");
450+
}
451+
452+
Component sw360Component;
453+
try {
454+
sw360Component = componentService.getComponentForUserById(id, user);
455+
} catch (Exception e) {
456+
throw new ResourceNotFoundException("Component not found with ID: " + id);
457+
}
458+
459+
if (sw360Component == null) {
460+
throw new ResourceNotFoundException("Component not found with ID: " + id);
461+
}
462+
463+
if (updateComponentDto == null) {
464+
throw new BadRequestClientException("Component data cannot be null");
465+
}
466+
467+
return sw360Component;
419468
}
420469

421470
@PreAuthorize("hasAuthority('WRITE')")

rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/JacksonCustomizations.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ static abstract class ComponentMixin extends Component {
652652
"licenseIdsSize",
653653
"licenseIdsIterator",
654654
"createdBy",
655+
"comment",
655656
"setId",
656657
"setRevision",
657658
"setType",
@@ -672,6 +673,7 @@ static abstract class ComponentMixin extends Component {
672673
"setVcs",
673674
"setPackageManager",
674675
"setRelease",
676+
"setComment",
675677
"createdByIsSet",
676678
"createdOnIsSet",
677679
"versionIsSet",
@@ -693,6 +695,7 @@ static abstract class ComponentMixin extends Component {
693695
"homepageUrlIsSet",
694696
"hashIsSet",
695697
"packageManagerIsSet",
698+
"commentIsSet",
696699
"typeIsSet"
697700
})
698701
static abstract class PackageMixin extends Package {

rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/integration/ComponentTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ public void should_update_component_invalid() throws IOException, TException {
308308
HttpMethod.PATCH,
309309
new HttpEntity<>(body, headers),
310310
String.class);
311-
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
311+
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
312312
}
313313

314314
@Test

rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ComponentSpecTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ public void before() throws TException, IOException {
331331
)
332332
);
333333
given(this.componentServiceMock.deleteComponent(eq(angularComponent.getId()), any())).willReturn(RequestStatus.SUCCESS);
334+
given(this.componentServiceMock.updateComponent(any(), any())).willReturn(RequestStatus.SUCCESS);
334335
given(this.componentServiceMock.searchByExternalIds(eq(externalIds), any())).willReturn((new HashSet<>(componentList)));
335336
given(this.componentServiceMock.convertToEmbeddedWithExternalIds(eq(angularComponent))).willReturn(
336337
new Component("Angular")

0 commit comments

Comments
 (0)