Skip to content

Commit

Permalink
Merge pull request #521 from BBMRI-ERIC/feat/add_rejection_reason
Browse files Browse the repository at this point in the history
feat: add message to status change for negotiations
  • Loading branch information
RadovanTomik authored Dec 9, 2024
2 parents ef8d63d + 4e10e53 commit f09c348
Show file tree
Hide file tree
Showing 19 changed files with 414 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@ public interface NegotiationAccessManager {
/**
* Verifies that the user has read access on public attributes of a Negotiation.
*
* @param negotiationId the ID of the Negotiation,=
* @param negotiationId the ID of the Negotiation
* @param userID the ID of the User
* @throws eu.bbmri_eric.negotiator.common.exceptions.ForbiddenRequestException if the user does
* not have access.
*/
void verifyReadAccessForNegotiation(String negotiationId, Long userID)
throws ForbiddenRequestException;

/**
* Verifies that the user can update a Negotiation
*
* @param negotiationId the ID of the Negotiation
* @param userID the ID of the User
* @throws ForbiddenRequestException if the user does not have access
*/
void verifyUpdateAccessForNegotiation(String negotiationId, Long userID)
throws ForbiddenRequestException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import eu.bbmri_eric.negotiator.common.AuthenticatedUserContext;
import eu.bbmri_eric.negotiator.common.exceptions.ForbiddenRequestException;
import eu.bbmri_eric.negotiator.user.PersonRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

@Service
public class NegotiationAccessManagerImpl implements NegotiationAccessManager {
Expand All @@ -25,4 +27,13 @@ public void verifyReadAccessForNegotiation(String negotiationId, Long userID) {
throw new ForbiddenRequestException("You are not allowed to perform this action");
}
}

@Override
public void verifyUpdateAccessForNegotiation(String negotiationId, Long userID)
throws ForbiddenRequestException {
if (!AuthenticatedUserContext.isCurrentlyAuthenticatedUserAdmin()
&& !negotiationRepository.existsByIdAndCreatedBy_Id(negotiationId, userID)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import eu.bbmri_eric.negotiator.governance.resource.dto.ResourceWithStatusDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationCreateDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationEventMetadataDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationFilterDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationUpdateDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationUpdateLifecycleDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.UpdateResourcesDTO;
import eu.bbmri_eric.negotiator.negotiation.mappers.NegotiationModelAssembler;
import eu.bbmri_eric.negotiator.negotiation.state_machine.negotiation.NegotiationEvent;
Expand Down Expand Up @@ -158,16 +160,34 @@ public EntityModel<NegotiationDTO> retrieve(@Valid @PathVariable String id) {
*
* @param id of the negotiation
* @param event from NegotiationEvents
* @param negotiationUpdateLifecycleDTO an optional body with details about the event
* @return NegotiationDTO with updated state if valid
*/
@PutMapping("/negotiations/{id}/lifecycle/{event}")
@PutMapping(
value = "/negotiations/{id}/lifecycle/{event}",
consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> sendEvent(
@Valid @PathVariable String id, @Valid @PathVariable("event") NegotiationEvent event) {
negotiationLifecycleService.sendEvent(id, event);
@Valid @PathVariable String id,
@Valid @PathVariable("event") NegotiationEvent event,
@RequestBody(required = false) @Nullable
NegotiationUpdateLifecycleDTO negotiationUpdateLifecycleDTO) {
String message = getOptionalComment(negotiationUpdateLifecycleDTO);
negotiationLifecycleService.sendEvent(id, event, message);
NegotiationDTO result = negotiationService.findById(id, true);
return ResponseEntity.ok(result);
}

@Nullable
private static String getOptionalComment(
@Nullable NegotiationUpdateLifecycleDTO negotiationUpdateLifecycleDTO) {
String message = null;
if (negotiationUpdateLifecycleDTO != null
&& negotiationUpdateLifecycleDTO.getMessage() != null) {
message = negotiationUpdateLifecycleDTO.getMessage();
}
return message;
}

/**
* Interact with the state of a resource in a negotiation by sending an Event
*
Expand Down Expand Up @@ -199,9 +219,13 @@ public ResponseEntity<?> sendEventForNegotiationResource(
* @return a list of possible events to send
*/
@GetMapping("/negotiations/{id}/lifecycle")
List<String> getPossibleEvents(@Valid @PathVariable String id) {
List<NegotiationEventMetadataDTO> getPossibleEvents(@Valid @PathVariable String id) {
return negotiationLifecycleService.getPossibleEvents(id).stream()
.map((obj) -> Objects.toString(obj, null))
.map(
(event) ->
new NegotiationEventMetadataDTO(
event.getValue(), event.getLabel(), event.getDescription()))
.sorted((e1, e2) -> e1.getValue().compareTo(e2.getValue()))
.collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package eu.bbmri_eric.negotiator.negotiation.dto;

import eu.bbmri_eric.negotiator.negotiation.state_machine.negotiation.NegotiationEvent;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.hateoas.server.core.Relation;

/** DTO for {@link eu.bbmri_eric.negotiator.database.model.NegotiationEventMetadata} */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Relation(collectionRelation = "events", itemRelation = "event")
public class NegotiationEventMetadataDto implements Serializable {
private NegotiationEvent value;
public class NegotiationEventMetadataDTO implements Serializable {
private String value;
private String label;
private String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package eu.bbmri_eric.negotiator.negotiation.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.lang.Nullable;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class NegotiationUpdateLifecycleDTO {
@Nullable private String message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationEventMetadataDto;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationEventMetadataDTO;
import eu.bbmri_eric.negotiator.negotiation.state_machine.negotiation.NegotiationEvent;
import eu.bbmri_eric.negotiator.negotiation.state_machine.negotiation.NegotiationLifecycleController;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -17,23 +18,25 @@
@Component
public class NegotiationEventAssembler
implements RepresentationModelAssembler<
NegotiationEventMetadataDto, EntityModel<NegotiationEventMetadataDto>> {
NegotiationEventMetadataDTO, EntityModel<NegotiationEventMetadataDTO>> {

@Override
public @NonNull EntityModel<NegotiationEventMetadataDto> toModel(
@NonNull NegotiationEventMetadataDto entity) {
public @NonNull EntityModel<NegotiationEventMetadataDTO> toModel(
@NonNull NegotiationEventMetadataDTO entity) {
List<Link> links = new ArrayList<>();
links.add(
linkTo(methodOn(NegotiationLifecycleController.class).getAllEvents()).withRel("events"));
links.add(
linkTo(methodOn(NegotiationLifecycleController.class).getEvent(entity.getValue()))
linkTo(
methodOn(NegotiationLifecycleController.class)
.getEvent(NegotiationEvent.valueOf(entity.getValue())))
.withSelfRel());
return EntityModel.of(entity, links);
}

@Override
public @NonNull CollectionModel<EntityModel<NegotiationEventMetadataDto>> toCollectionModel(
@NonNull Iterable<? extends NegotiationEventMetadataDto> entities) {
public @NonNull CollectionModel<EntityModel<NegotiationEventMetadataDTO>> toCollectionModel(
@NonNull Iterable<? extends NegotiationEventMetadataDTO> entities) {
return RepresentationModelAssembler.super.toCollectionModel(entities);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public NegotiationModelAssembler(
for (NegotiationEvent event : negotiationLifecycleService.getPossibleEvents(entity.getId())) {
entityModel.add(
WebMvcLinkBuilder.linkTo(
methodOn(NegotiationController.class).sendEvent(entity.getId(), event))
methodOn(NegotiationController.class).sendEvent(entity.getId(), event, null))
.withRel(event.toString())
.withTitle("Next Lifecycle event")
.withName(event.getLabel()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package eu.bbmri_eric.negotiator.negotiation.state_machine.negotiation;

import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationEventMetadataDto;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationEventMetadataDTO;
import eu.bbmri_eric.negotiator.negotiation.dto.NegotiationStateMetadataDto;
import eu.bbmri_eric.negotiator.negotiation.mappers.NegotiationEventAssembler;
import eu.bbmri_eric.negotiator.negotiation.mappers.NegotiationStateAssembler;
Expand Down Expand Up @@ -59,20 +59,20 @@ public EntityModel<NegotiationStateMetadataDto> getState(

@GetMapping(value = "/negotiation-lifecycle/events")
@Operation(summary = "Retrieve metadata about all possible negotiation events")
public CollectionModel<EntityModel<NegotiationEventMetadataDto>> getAllEvents() {
public CollectionModel<EntityModel<NegotiationEventMetadataDTO>> getAllEvents() {
return negotiationEventAssembler.toCollectionModel(
Arrays.stream(NegotiationEvent.class.getEnumConstants())
.map(
negotiationState ->
modelMapper.map(negotiationState, NegotiationEventMetadataDto.class))
modelMapper.map(negotiationState, NegotiationEventMetadataDTO.class))
.collect(Collectors.toSet()));
}

@GetMapping(value = "/negotiation-lifecycle/events/{event}")
@Operation(summary = "Retrieve metadata about all a specific negotiation event")
public EntityModel<NegotiationEventMetadataDto> getEvent(
public EntityModel<NegotiationEventMetadataDTO> getEvent(
@Valid @PathVariable NegotiationEvent event) {
return negotiationEventAssembler.toModel(
modelMapper.map(event, NegotiationEventMetadataDto.class));
modelMapper.map(event, NegotiationEventMetadataDTO.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,13 @@ public interface NegotiationLifecycleService {
*/
NegotiationState sendEvent(String negotiationId, NegotiationEvent negotiationEvent)
throws WrongRequestException, EntityNotFoundException;

/**
* Send an event to a particular Negotiation also specifying a message with reason why.
*
* @return the new status of the Negotiation
*/
NegotiationState sendEvent(
String negotiationId, NegotiationEvent negotiationEvent, String message)
throws WrongRequestException, EntityNotFoundException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,34 @@ public Set<NegotiationEvent> getPossibleEvents(String negotiationId)
@Override
public NegotiationState sendEvent(String negotiationId, NegotiationEvent negotiationEvent)
throws WrongRequestException, EntityNotFoundException {
changeStateMachine(negotiationId, negotiationEvent);
changeStateMachine(negotiationId, negotiationEvent, null);
return getCurrentStateForNegotiation(negotiationId);
}

private void changeStateMachine(String negotiationId, NegotiationEvent negotiationEvent) {
@Override
public NegotiationState sendEvent(
String negotiationId, NegotiationEvent negotiationEvent, String message)
throws WrongRequestException, EntityNotFoundException {
changeStateMachine(negotiationId, negotiationEvent, message);
return getCurrentStateForNegotiation(negotiationId);
}

private void changeStateMachine(
String negotiationId, NegotiationEvent negotiationEvent, String message) {
if (!getPossibleEvents(negotiationId).contains(negotiationEvent)) {
throw new ForbiddenRequestException(
"You are not allowed to %s the Negotiation"
.formatted(negotiationEvent.getLabel().toLowerCase()));
}

persistStateMachineHandler
.handleEventWithStateReactively(
MessageBuilder.withPayload(negotiationEvent.name())
.setHeader("negotiationId", negotiationId)
.setHeader("postBody", message)
.setHeader(
"postSenderId",
AuthenticatedUserContext.getCurrentlyAuthenticatedUserInternalId())
.build(),
getCurrentStateForNegotiation(negotiationId).name())
.subscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ public class NegotiationStateChangeEvent extends ApplicationEvent {
private final String negotiationId;
private final NegotiationState changedTo;
private final NegotiationEvent event;
private final String post;

public NegotiationStateChangeEvent(
Object source, String negotiationId, NegotiationState changedTo, NegotiationEvent event) {
Object source,
String negotiationId,
NegotiationState changedTo,
NegotiationEvent event,
String post) {
super(source);
this.negotiationId = negotiationId;
this.changedTo = changedTo;
this.event = event;
this.post = post;
}
}
Loading

0 comments on commit f09c348

Please sign in to comment.