1 package de.juplo.kafka.payment.transfer.adapter;
4 import de.juplo.kafka.payment.transfer.domain.Transfer;
5 import de.juplo.kafka.payment.transfer.ports.GetTransferUseCase;
6 import de.juplo.kafka.payment.transfer.ports.MessagingService;
7 import lombok.RequiredArgsConstructor;
8 import lombok.extern.slf4j.Slf4j;
9 import org.springframework.http.HttpStatus;
10 import org.springframework.http.MediaType;
11 import org.springframework.http.ResponseEntity;
12 import org.springframework.validation.FieldError;
13 import org.springframework.web.bind.MethodArgumentNotValidException;
14 import org.springframework.web.bind.annotation.*;
15 import org.springframework.web.context.request.async.DeferredResult;
16 import org.springframework.web.reactive.function.client.WebClient;
17 import reactor.core.publisher.Mono;
19 import javax.servlet.http.HttpServletRequest;
20 import javax.validation.Valid;
22 import java.util.Date;
23 import java.util.HashMap;
25 import java.util.Optional;
26 import java.util.concurrent.CompletableFuture;
29 @RequestMapping(TransferController.PATH)
31 @RequiredArgsConstructor
33 public class TransferController
35 public final static String PATH = "/transfers";
37 private final GetTransferUseCase getTransferUseCase;
38 private final MessagingService messagingService;
39 private final TransferConsumer consumer;
40 private final WebClient webClient;
45 consumes = MediaType.APPLICATION_JSON_VALUE,
46 produces = MediaType.APPLICATION_JSON_VALUE)
47 public DeferredResult<ResponseEntity<?>> transfer(@Valid @RequestBody TransferDTO transferDTO)
49 DeferredResult<ResponseEntity<?>> result = new DeferredResult<>();
51 Long id = transferDTO.getId();
54 .uriForKey(id.toString())
57 .uri(uri + PATH + "/" + id)
58 .accept(MediaType.APPLICATION_JSON)
60 .onStatus(status -> true, bar -> Mono.empty())
64 resp.getStatusCode().is2xxSuccessful()
65 ? Optional.of(Boolean.TRUE)
66 : Optional.<Boolean>empty()))
67 .or(() -> Optional.of(getTransferUseCase.get(transferDTO.getId()).map(transfer -> Boolean.TRUE)))
68 .flatMap(optional -> optional)
70 CompletableFuture.completedFuture(
73 .location(location(transferDTO))
81 .id(transferDTO.getId())
82 .payer(transferDTO.getPayer())
83 .payee(transferDTO.getPayee())
84 .amount(transferDTO.getAmount())
88 .created(location(transferDTO))
91 .thenAccept(responseEntity -> result.setResult(responseEntity))
94 result.setErrorResult(e);
101 private URI location(TransferDTO transferDTO)
103 return URI.create(PATH + "/" + transferDTO.getId());
108 produces = MediaType.APPLICATION_JSON_VALUE)
109 public ResponseEntity<TransferDTO> get(@PathVariable Long id)
113 .uriForKey(Long.toString(id))
116 ResponseEntity<TransferDTO> response =
118 .status(HttpStatus.TEMPORARY_REDIRECT)
119 .location(URI.create(uri + PATH + "/" + id))
126 .map(transfer -> ResponseEntity.ok(TransferDTO.of(transfer)))
127 .orElse(ResponseEntity.notFound().build()));
130 @ResponseStatus(HttpStatus.BAD_REQUEST)
131 @ExceptionHandler(MethodArgumentNotValidException.class)
132 public Map<String, Object> handleValidationExceptions(
133 HttpServletRequest request,
134 MethodArgumentNotValidException e)
136 Map<String, Object> errorAttributes = new HashMap<>();
137 errorAttributes.put("status", HttpStatus.BAD_REQUEST.value());
138 errorAttributes.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
139 errorAttributes.put("path", request.getRequestURI());
140 errorAttributes.put("method", request.getMethod());
141 errorAttributes.put("timestamp", new Date());
142 Map<String, String> errors = new HashMap<>();
143 e.getBindingResult().getAllErrors().forEach((error) -> {
144 String fieldName = ((FieldError) error).getField();
145 String errorMessage = error.getDefaultMessage();
146 errors.put(fieldName, errorMessage);
148 errorAttributes.put("errors", errors);
149 errorAttributes.put("message", "Validation failed: Invalid message format, error count: " + errors.size());
150 return errorAttributes;