Reputation: 670
I have a controller which is asking a service to reach a service endpoint to get a list of account numbers, then reach out to another service for each account number and retrieve additional data about that account. The 'happy path' essentially looks like this:
CONTROLLER
@ApiOperation(value = "getRelatedAccounts")
@GetMapping(value = "/relatedaccounts")
public ResponseEntity<RelatedAccounts> getRelatedAccounts(@RequestParam String accountNumber) {
return new ResponseEntity<>(myService.getRelatedAccounts(accountNumber), HttpStatus.OK);
}
SERVICE
public RelatedAccounts getRelatedAccounts(String accountNumber) {
// blah blah, hit the endpoint and get my response of a list of account numbers in relatedAccountsList
Flux<AccountInformation> accountInfoFlux = Flux.fromIterable(relatedAccountsList)
.parallel()
.runOn(Schedulers.elastic())
.flatMap(this::getAccountInformation)
.ordered(Comparator.comparing(RelatedAccounts::getCorrectOrder)) // blah blah, convert from ParallelFlux to Flux
}
OTHER SERVICE
public Mono<AccountInformation> getAccountInformation(String accountNumber) {
WebClient webClient = WebClient.builder()
.baseUrl("http://myurl.com")
.build();
return webClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("accountNumber", accountNumber)
.build()
).retrieve()
.bodyToMono(AccountInformation.class) // This doesn't work for us, we get a very complex object as a response and need to parse a few fields.
}
I've spent all day on Google and I don't see anyone else parsing the response they get back, they just magically map it directly onto a perfectly created class. We don't have that option, we need to pluck the accountName from the response body and put it in the AccountInformation object. Does anyone have any idea how to do this?
Upvotes: 2
Views: 9245
Reputation: 670
We ended up doing it a pretty ugly, but extensible and effective way. In our OTHER SERVICE:
public Mono<AccountInformation> getAccountInformation(String accountNumber) {
WebClient webClient = WebClient.builder()
.baseUrl("http://myurl.com")
.build();
return webClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("accountNumber", accountNumber)
.build()
).retrieve()
.bodyToMono(String.class) // just get the response as a JSON string
.map(jsonString -> {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonString);
// parse/ map out all your information here
return myCustomMappedObject;
})
}
Upvotes: 0
Reputation: 2453
My suggestion is to stay with bodyToMono(AccountInformation.class)
and then map into your simple object using Monos map and zip.
.zip
to combine your simple object with the AccountInformation object.Eg.
Other service stays the same:
public Mono<AccountInformation> getAccountInformation(String accountNumber) {
WebClient webClient = WebClient.builder()
.baseUrl("http://myurl.com")
.build();
return webClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("accountNumber", accountNumber)
.build()
).retrieve()
.bodyToMono(AccountInformation.class)
}
Create the missing class (which represents the result of the other service), skipp all fields for infos you dont need
import java.util.List;
public class AccountInformation {
private String infoYouWant;
private List<AccountDetails> otherNestedInfoYouWant;
// getters/setters skipped for easier read
}
Combine your simple object with the one from the other service:
Mono<AccountInformation> accountInformation = someService.getAccountInformation("fillMeWithAccountNumber");
Mono<SimpleObject> simpleObjectWithAccountInformation = Mono.zip(simpleObject, accountInformation).map(objects -> {
SimpleObject simpleObject = objects.getT1();
simpleObject.setAccountNumber(objects.getT2().getInfoYouWant());
return simpleObject;
});
Upvotes: 2