How to get body from Mono<ClientResponse>? - java

I am trying to implement the BodyExtractor interface get the body from Mono< ClientResponse> as an Object instead of it in Mono.
I could not find any example of BodyExtractor implementation. I am wondering is this a good idea to implement it or is there any other way to get the body as an object.
Below is the code line that I currently have
public Mono&ltResponseEntity&ltMono&ltJsonNode&gt&gt&gt processUnmappedApiRequest(ServerHttpRequest request, JsonNode body) {
RequestData reqData = this.prepareReqMetadata(request, body);
Mono&ltClientResponse&gt response = commonConnector.getApiResponse(reqData);
return response.map(respData -> {
int latestVersion = respData.headers().header("version").size() == 0 ? getLatestVersion(request) :
Integer.parseInt(respData.headers().header("version").get(0));
List converterList;
if((converterList = converterSequenceProvider.getConverterList(reqData.getRequestPath(), latestVersion, reqData.getVersion())) != null){
return ResponseEntity.ok().body(respData.bodyToMono(JsonNode.class).map(respBody -> convertToDesiredVersion(converterList, respBody)));
}
return ResponseEntity.ok().body(respData.bodyToMono(JsonNode.class));
});
}
In this method my return type is Mono< ResponseEntity< Mono< JsonNode>>> and I am trying to convert it to Mono< ResponseEntity< JsonNode>> because my team is not agreeing with Mono inside a Mono.
So the main point here is I don't want to use bodyToMono method and I am not sure how to use body method.
Please help me out here.

If you are trying to return just a Mono object, you can use the flatMap method instead of map, so you can avoid something like Mono<Mono<X>> and get just Mono<X>.
map
Transform the item emitted by this Mono by applying a synchronous
function to it.
flatMap
Transform the item emitted by this Mono asynchronously, returning the
value emitted by another Mono (possibly changing the value type).
Also, there is a method on ServerResponse.BodyBuilder syncBody which can take normal body and return it in Mono. Map function's argument is already a non wrapped object, so you can do something like:
JsonNode jsonNode=transform(clientResponse);
return ResponseEntity.ok().syncbody(jsonNode);

Related

WebClient doesn't fetch data as expected

I'm trying to reactively fetch data from external API using two methods from some Service class.
I'm new to reactive and Spring in general, so it could be a very obvious mistake but I just can't find it
These are the two methods:
public Mono<SomeClass> get(int value) {
return webClient.get()
.uri("/" + value)
.retrieve()
.onRawStatus(HttpStatus.CONFLICT::equals, clientResponse -> {
return Mono.error(new SomeException1("Some message", clientResponse.rawStatusCode()));
})
.onRawStatus(HttpStatus.NOT_FOUND::equals, clientResponse -> {
return requestGeneration(value)
.flatMap(res -> Mono.error(new SomeException1("Some message", clientResponse.rawStatusCode())));
})
.bodyToMono(SomeClass.class)
.retryWhen(Retry.backoff(5, Duration.ofSeconds(8))
.filter(throwable -> throwable instanceof SomeException1));
}
private Mono<Void> requestGeneration(int value) {
return webClient.post()
.uri("/" + value)
.retrieve()
.onRawStatus(HttpStatus.BAD_REQUEST::equals, clientResponse -> {
return Mono.error(new SomeException2("Wrong value", clientResponse.rawStatusCode()));
})
.bodyToMono(Void.class);
}
Baasically what I'm trying to achieve is:
first GET from http://api.examplepage.com/{value}
if that API returns HTTP404 it means I need to first call POST to the same URL, because the data is not yet generated
the second function does the POST call and returns Mono<Void> because it is just HTTP200 or HTTP400 on bad generation seed (i don't need to process the response)
first function (GET call) could also return HTTP429 which means the data is generating right now, so I need to call again after some time period (5-300 seconds) and check if data has been generated already
then after some time it results in HTTP200 with generated data which I want to map to SomeClass and then return mapped data in controller below
#PostMapping("/follow/{value}")
public Mono<ResponseEntity<?>> someFunction(#PathVariable int value) {
return Mono.just(ResponseEntity.ok(service.get(value)));
}
all the code I posted is very simplified to the issues I'm struggling with and doesn't contain some things I think are not important in this question
and now the actual question:
it doesn't actually make the call? i really don't know what is happening
program doesn't enter onRawStatus, even if i change it to onStatus 2xx or whatever other httpstatus and log inside i see nothing as if it doesn't even enter the chains
when i manually call with postman it seems like the program calls have never been made because the GET call returns 404 (the program didn't request to generate data)
the controller only returns "scanAvailable": true, when i expect it to return mapped SomeClass json
// edit
i changed the code to be a full chain as suggested and it didn't solve the problem. all the status are still unreachable (code inside any onStatus nor onRawStatus never executes)

Dealing with two different types in RxJava's .flatMap() operator

The application i'm writing performs an initial API call with Retrofit which returns a URL. That response is then .flatMap'd into another API call depending on the text contained in the URL. However, the two secondary API calls are defined to return different response models.
To make things clearer, here is some code:
APIService service = retrofit.create(APIService.class);
service.getURL() // returns response model containing a URL.
.flatMap(new Function<GetURLResponse, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(GetURLResponse getURLResponse) throws Exception {
// Determine whether the returned url is for "first.com".
if (getURLResponse.url.contains("first.com")) {
return service.first(getURLResponse.url);
}
// Otherwise, the URL is not for "first.com", so use our other service method.
return service.second(getURLResponse.url);
}
})
Here are the interface definitions for service.first() and service.second():
#GET
Observable<retrofit2.Response<ResponseBody>> first(#Url String url);
#GET
Observable<SecondModel> second(#Url String url);
How can I better handle these two different possible types (retrofit2.Response<ResponseBody> and SecondModel) for the rest of the stream? Eg. If the initial URL contains first.com then the service.first() API call should fire, and operators down the stream should received a retrofit2.Response<ResponseBody>. Conversely, if the initial URL does not contain first.com, the service.second() API call should fire and operators down the stream should receive a SecondModel.
The easiest way would be to have both your model classes implement an interface and return that interface, alternatively the models could both extend an abstract class to achieve the same effect. You would then do an instanceOf check to see which model it is and continue with your preferred transformations.
That having said you mentioning downstream operators, makes me think that this would cause an annoying amount of checks. So what I would do is split the stream using the publish operator, and then apply your further transformations to each sub-stream. Finally you should merge the two streams back together and return a single model encompassing both models.
Below a code example to get you started.
Observable<Integer> fooObservableSecondaryRequest(String foo) {
return Observable.just(1);
}
Observable<Integer> booObservableSecondaryRequest(String boo) {
return Observable.just(2);
}
Observable<String> stringObservable = Observable.just("foo", "boo");
stringObservable.publish(shared -> Observable.merge(
shared.filter(a -> a.equals("foo"))
.flatMap(fooString -> fooObservableSecondaryRequest(fooString))
.map(num -> num * 2),
shared.filter(a -> a.equals("boo"))
.flatMap(booString -> booObservableSecondaryRequest(booString))
.map(num -> num * 10)
)).subscribe(result -> System.out.println(result)); // will print 2 and 20

Java Optional return value only if present

Is it possible to return the Optional value from method only if it's present, but if it's not just continue with a normal method flow. Something like this:
public Message processMessage(Message message) {
// ErrorMessage is subclass of Message; only returned if validator found some violations, otherwise empty optional
Optional<ErrorMessage> error = validator.validate(message);
if (error.isPresent()) return error.get();
// returns different possible subclasses of Message
return service.processMessage(message);
}
Is there any prettier way to write this?
Your problem is indeed that the return types do not match. orElseGet only works when the supplier returns a subtype of the Optional type.
To work around this, you can force a cast to the parent type first:
return error.<Message>map(m -> m).orElseGet(() -> service.processMessage(message));
or equivalently:
return error.map(m -> (Message) m).orElseGet(() -> service.processMessage(message));
I simply would go with Optional.orElseGet and an explicit cast in between:
public Message processMessage(final Message message) {
return validator.validate(message)
.map(Message.class::cast)
.orElseGet(() -> service.processMessage(message));
}
I am not able to test it currently but it may give u an idea, may be you can implement something like below
return Optional.ofNullable(message)
.map(validator::validate)
.orElseGet(service::processMessage)
Looking at that snippet, it looks like the validate method on the validator doesn't quite have the proper return type. Consider using e.g. io.vavr.control.Either or io.vavr.control.Validation monad. Take a look at a possible signature for validate:
Either<SpecificError, ? extends Message> validate(Message message);
Now the map and fold combinators can be used like so:
validator
.validate(message)
.map(message -> service.processMessage(message))
.fold(error -> new ErrorMessage(error), success -> success); // the left function can be replaced by Function.identity()
to yield Message.

F.Promise<Result> and nested async promises

I am using F.Promise in my async action in Playframework 2.4.6. I am able to get async calls to my DAO to work. I am able to use "map" and "flatMap" but I am not sure about the following situation.
I have an async call to find and object from the database. If that object is found, I then want to use parts of that object to then issue another async request to update that object in the database. This is part of an async update call in my controller. I am not doing something right though.
I am starting off with the following.
F.Promise<User> findUserPromise = userService.findAsync(id);
F.Promise<User> updateUserPromise = userService.updateAsync(updatedUser);
F.Promise<Result> resultPromise = findUserPromise.flatMap((foundUser){
// update foundUser with passed in Json criteria
foundUser.firstName = firstName; // etc...
return updateUserPromise.map((updatedUser) -> {
return ok(Json.toJson(u));
});
});
return resultPromise;
This doesn't seem to work. I do not want to define my updatedUserPromise until I get a response back from my findUserPromise, because maybe a user will not be found. If a user is not found, I will return F.Promise.pure(notFound("some json result")). If a user is found, then I want to update that user with the Json criteria that gets posted to my controller action. With my approach, it seems that I have to define the updateUserPromise's async call with an object (updatedUser) before I get back any results.
Can I define another promise inside the result of a promise?
I was thinking something like this, but this fails with compilation errors. inference variable B has incompatible bounds, equality constraints: play.mvc.Result, lower bounds: Play.libs.F.Promise.
F.Promise<User> findUserPromise = userService.findAsync(id);
F.Promise<Result> resultPromise = findUserPromise.map((foundUser) -> {
// foundUser is returned, now update it, or return not found...
// define our next promise with an updated foundUser object
F.Promise<User> userPromise = userService.updateAsync(foundUser);
F.Promise<Result> resultPromise2 = userPromise.map((u) -> {
return jsonResult(ok(Json.toJson(u)));
});
return resultPromise2;
});
return resultPromise;
Thanks for any help.
I am not sure if this is best way to do this, but this is working for my situation above.
F.Promise<User> findUserPromise = userService.findAsync(id);
return findUserPromise.flatMap((fu) -> {
if (fu == null) {
ObjectNode result = Json.newObject();
result.put("error", "Not found " + id);
return F.Promise.pure(jsonResult(notFound(result)));
}
// dynamically update fields on found user
fu.userName = userName;
fu.firstName = firstName;
// more updates...
// make a new promise that will use the updated foundUser object
F.Promise<User> updateUserPromise = userService.updateAsync(fu);
return updateUserPromise.map((uu) -> {
return jsonResult(ok(Json.toJson(uu)));
});
}
All the examples I found for chaining promises or nested promises, they seem to use web services as an example, and they all appear to be known at design time. In my situation, I do not know what my foundUser object will be until it is returned, so I need to create my inner promise inside the outer promise.

java nested http call api library

I was reading some http webservice api from playframework, link below.
I am not able to understand how this nested work with the flatmap.
Can someone give me some hints how to crack down this big chunk of functions call.
from http://www.playframework.com/documentation/2.2.x/JavaWS
Composing results
If you want to make multiple calls in sequence,
this can be achieved using flatMap:
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url(feedUrl).get().flatMap(
new Function<WS.Response, Promise<Result>>() {
public Promise<Result> apply(WS.Response response) {
return WS.url(response.asJson().findPath("commentsUrl").asText()).get().map(
new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok("Number of comments: " + response.asJson().findPath("count").asInt());
}
}
);
}
}
);
return resultPromise;
}
flatMap and map are common Scala (or more generally, functional programming) functions. They both accept a function as a parameter. In order to translate the Play WS API into Java (and pretty much everything else), Scala's function type needed to be re-implemented in Java, so that you can take full advantage of the WS library. It's being done here in a similar way the Scala compiler does it. Function<A,B> is an abstract type that requires an apply method. The parameter(s) of apply are the parameters of the function, and the return type of apply is that of the function.
If you have this function in Java:
public String int2String(Integer integer) {
return integer.toString();
}
It would be equivalent to this:
new Function<Integer, String>() {
public String apply(Integer integer) {
return integer.toString();
}
}
Let's start simpler with the case of just one WS call. WS.url(...).get() returns a Promise<WS.Response>. Promise is a container class for a promised value. In order to handle the value it contains (or will eventually contain), we need to use the map function. For a Promise<WS.Response>, map will accept a Function<WS.Response, T> as a parameter, where T is the type you want to map the response to.
As an example, let's define a Function that will just return the body of the WS.Response in a Play HTTP Result:
Function<WS.Response, Result> echo = new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok(response.asText());
}
}
Now let's use this Function in a WS call in a controller:
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url("http://google.com").get().map(echo);
return resultPromise;
}
Once the Promise has been fulfilled, the echo function defined earlier will be executed inside map, returning the Promise<Result>. The two previous blocks of code could also be written like this (combined into one with an anonymous function):
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url("http://google.com").get().map(
new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok(response.asText());
}
}
);
return resultPromise;
}
As a crude example, let's say we need to make two WS calls. The second WS call will depend on the first. Perhaps the first call will give us some URL we will use to make the second WS call.
This is where flatMap comes into picture. We will need two functions to accomplish this task. The first function is what is passed to flatMap, which will be executed when the first WS.Response is received. This first function will use the first response to make the second WS call, which returns another Promise<WS.Response> that must be mapped to get our final result. So we map the second result with a second function that translates the WS.Response into our Result.
So what happened? If we used map instead of flatMap in both instances, the chain of events would go something like this:
The first get() returned a Promise<WS.Response>, then we map the contained WS.Response to a Promise<Result>. That, however, would leave us with a Promise<Promise<WS.Response>>, which is not very desirable. Using flatMap in the outer function instead will flatten the Promises into a single Promise<Result>. Simiarly, if you were doing 3 or more nested calls, you would map each result to an inner function, and have just one flatMap at the outer level to flatten everything at the end.
This all of course looks much more beautiful in Scala.

Categories