How to perform Junit Test for DELETE method in Reactive Programming? - spring-webclient

I am trying to use WebClient DELETE method. I am making synchronous call for DELETE method.
But, when I am using block() method to get the object. I am getting error.
#Test
public void deleteEmployeeSyncTest() {
log.info("Testing put employee request");
Map<String, String> headersMap = new HashMap<String, String>();
headersMap.put("user", "password");
MultiValueMap<String, String> queryParamsMap = new LinkedMultiValueMap<>();
queryParamsMap.put("idmProperty", Arrays.asList("queryparamavalue"));
Map<String, String> pathParamsMap = new HashMap<>();
pathParamsMap.put("id", "1");
WebClient.ResponseSpec deleteResponse =
restClient.deleteSync(clientIdm, restClientConfig.getEndpoints().get("idm"), "deleteEmployee",
headersMap, pathParamsMap, queryParamsMap);
Employee response = deleteResponse.bodyToMono(Employee.class).block();
log.info("Delete employee response results: {}", response);
//response.subscribe(result -> Assertions.assertNull(result));
}
It gives me error while getting response using block() method call. But, if I use subscribe() method call for Asynchronous transactions, it works.
Here is the error I am getting while running the test case.
org.springframework.web.reactive.function.client.WebClientResponseException$MethodNotAllowed: 405 Method Not Allowed from DELETE http://localhost:8080/test/delete/1?idmProperty=queryparamavalue
at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:204)

Related

How to Mock a ResponseEntity<?> method

I am implementing unit tests in Spring Boot but I can't get them to work.
Here is my method n the RoulletController class.
#PutMapping("/open_roulette/")
public ResponseEntity<?> enableRoluette(#RequestParam("id") Long id) {
return rouletteService.enableRouletteById(id);
}
Here is my method on the RouletteService class.
#Override
public ResponseEntity<?> enableRouletteById(Long roulette_id) {
Optional<Roulette> roulette = rouletteRepository.findById(roulette_id);
HashMap<String, Object> response = new HashMap<String, Object>();
if (roulette.isPresent()) {
Roulette request = roulette.get();
{
if (!request.isRouletteStatus())
request.setBets(null);
}
request.setRouletteStatus(true);
rouletteRepository.save(request);
response.put("message", "La ruleta ha sido activada con éxito");
response.put("roulette", request);
return new ResponseEntity<Map<String, Object>>(response, HttpStatus.CREATED);
} else {
response.put("message", "La apuesta no es correcta");
response.put("error", HttpStatus.BAD_REQUEST);
return new ResponseEntity<Map<String, Object>>(response, HttpStatus.BAD_REQUEST);
}
}
And here is my test method RoulletControllerTest class, i'm trying to do soemthing like this but i got "The method thenReturn is not applicable for the arguments, etc.
#Test
public void testEnableRoluette() {
HashMap<String, Object> response = new HashMap<String, Object>();
ResponseEntity<?> responseEntity = new ResponseEntity<Map<String, Object>>(response, HttpStatus.CREATED);
Mockito.when(rouletteService.enableRouletteById(14L)).thenReturn(responseEntity);
assertEquals(rouletteController.enableRoluette(14L), responseEntity);
}
Thank you.
It doesn't like the ? parameter of the ResponseEntity. It works if you give the real type, ie. ResponseEntity>.
The error message hints at that. It is expecting CAP#2 but is getting CAP#1:
ControllerTest.java:26: error: no suitable method found for thenReturn(ResponseEntity<CAP#1>)
.thenReturn (responseEntity);
^
method OngoingStubbing.thenReturn(ResponseEntity<CAP#2>) is not applicable
(argument mismatch; ResponseEntity<CAP#1> cannot be converted to ResponseEntity<CAP#2>)
method OngoingStubbing.thenReturn(ResponseEntity<CAP#2>,ResponseEntity<CAP#2>...) is not applicable
(argument mismatch; ResponseEntity<CAP#1> cannot be converted to ResponseEntity<CAP#2>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
You can also improve your code by moving the response handling to the controller. Your service should just returns the Map<>. This removes the dependency on the web layer and the service is easier to reuse in other code.

Sending request with headers to third parts api with WebClient

I really like the solution I have with RestTemplate but soon it will be depreciated with future spring releases. I am trying to send some text to a third party api using WebClient
String text = URLEncoder.encode(text,"UTF-8");
WebClient webClient = WebClient.builder()
.baseUrl(BASE_URL)
.defaultHeader("Key","af999-e99-4456-b556-4ef9947383d")
.defaultHeader("src", srcLang)
.defaultHeader("tgt", tgtLang)
.defaultHeader("text", text)
.build();
Then send a post here:
Mono<String> response = webClient.post().uri("/google/rtv/text")
.retrieve()
.bodyToMono(String.class);
Trying to parse based off of the legacy response:
private String parseJson( Mono<String> response) {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = null;
JsonNode review = null;
//TODO: create an object and map it here. We need to save the original review too.
try {
root = mapper.readTree(response.toString());
review = root.path("message");
} catch (IOException e) {
e.printStackTrace();
}
return review.asText();
}
Later I need to parse the response but right now I am getting an error saying:
com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'MonoFlatMap': was expecting ('true', 'false' or 'null')
at [Source: (String)"MonoFlatMap"; line: 1, column: 23]
and later:
java.lang.NullPointerException: null
What I am trying to accomplish is something like I have done with RestTemplate.
Like so:
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(URL)
.queryParam("src", src)
.queryParam("tgt", tgt)
.queryParam("text", text);
ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, request, String.class);
then set my header for the subscription globally.
private ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("Key","af999-e99-4456-b556-4ef9947383d");
ClientHttpResponse response = execution.execute(request, body);
return response;
}
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(this::intercept));
return restTemplate;
}
Advice?
The problem happens here:
root = mapper.readTree(response.toString());
This code snippet is trying to serialize a Mono<String> as a String, when a Mono is a reactive type that can provide that String value eventually.
You could call response.block() and getting the resulting String, but this would be a blocking call and Reactor forbids that if in the middle of a reactive execution. This is done for good reasons, since this will block one of the few threads that your web application is using and can cause it to stop serving other requests.
You could instead have something like:
Mono<String> review = response.map(r -> parseJson(r);
And then reuse that new value down the line.
Note that WebClient natively supports JSON deserialization and you could deserialize the whole payload like so:
Mono<Review> review = webClient.post().uri("/google/rtv/text")
.retrieve()
.bodyToMono(Review.class);

java.lang.IllegalArgumentException: Comparison method violates its general contract! in Spring Rest Template

I am facing a weird issue while calling a REST url using Spring's RestTemplate.I am using Spring Boot.
The error is occurring only after approx 10 to 15 successful calls and thereafter erratically. I can smoothly exchange data before the error, in the first 1 to 15 calls approx. Url is like someresturl/param1/param2/param3.
public ResponseEntity<String> callRestUrl(CustomReqClass req) {
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
StringBuilder url = new StringBuilder("someresturl");
finishTaskUrl.append("/").append(param1).append("/").append(param2).append("/").append(param3);
ResponseEntity<String> response = null;
HttpEntity<CustomReqClass> request = new HttpEntity<CustomReqClass>(req, getHTTPHeaders());
try {
//first approach
response = restTemplate.postForEntity(url.toString(), request, String.class, Collections.<String, String>emptyMap());
//second approach
response = restTemplate.exchange(url.toString(), HttpMethod.POST, request, String.class);
} catch (Exception e) {
LOGGER.info("Error calling url" + e);
}
return response;
}
public MultiValueMap<String, String> getHTTPHeaders() {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("Authorization", "Basic authabcdxyz");
headers.add("Content-Type", "application/json");
return headers;
}
Here I am autowiring restTemplate object in the class where I am using this.
I have tried both the above methods postForEntity and exchange of Rest template. Error is occurring for both.
The exception I am getting after first few successful attempts:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
As an additional thought, the above piece of code is being scheduled by Spring Scheduler mechanism. Is it possible internal threading used in scheduler is causing this issue?

Spring RestController handle Get Request with Body values

i currently develop an experimental prototype an wanted to ask if there is a way to accept a response body in a get request.
#RequestMapping(method=RequestMethod.GET, path="/stair/shippingorders", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> getShippingOrder(#RequestBody JsonNode request) throws JsonProcessingException, IOException{
log.info("get body: " + request);
// do stuff
return ResponseEntity.ok(response);
}
the test should looks something like this
#Test
public void shouldAcceptRequestBodyinGetRequest() {
JSONObject body = new JSONObject();
body.appendField("stuff", "{}");
HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(body);
ResponseEntity<String> result = restTemplate.exchange(GET_URL,HttpMethod.GET, entity, String.class );
assertNotNull(result);
}
GET method doesn't support body, hence it won't be possible to send body as part of request. The common practice is to use POST instead

Custom json response for internal exception in spring

While implementing a global exception handler in Spring, I noticed that in case of a not recognized Accept header, Spring would throw it's own internal error. What I need is to return a custom JSON error structure instead. Works fine for application specific exceptions and totally fails for Spring HttpMediaTypeNotAcceptableException.
This code tells me "Failed to invoke #ExceptionHandler method: public java.util.Map RestExceptionHandler.springMalformedAcceptHeaderException()" when I try to request a page with incorrect Accept header. Any other way to return custom JSON for spring internal exceptions?
#ControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
#ResponseBody
public Map<String, String> springMalformedAcceptHeaderException() {
Map<String, String> test = new HashMap<String, String>();
test.put("test", "test");
return test;
}
}
Eventually figured that the only way is to do the json mapping manually.
#ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
#ResponseBody
public String springMalformedAcceptHeaderException(HttpServletResponse response) {
// populate errorObj, set response headers, etc
ObjectWriter jsonWriter = new ObjectMapper().writer();
try {
return jsonWriter.writeValueAsString(errorObj);
} catch(Exception e){}
return "Whatever";
}

Resources