Issue
I am slightly confused to get the proper response when the user tries to use two rest endpoints with WebClient. I want to use it as asynchronous and non-blocking in the code.
I am returning the Flux from the controller. The code details are as below:
The Controller Class method looks like this:
@RequestMapping(method = RequestMethod.GET, path = "/v1/repos/{userName}", produces = "application/json")
public ResponseEntity<Flux<GithubRepo>> getUserReposDetails(
@PathVariable(name="userName",required = true) String userName) throws Exception{
return new ResponseEntity<>(this.getGitRepos(userName), HttpStatus.OK);
}
It is calling getGitRepos method. The method details are as below:
private Flux<GithubRepo> getGitRepos(String userName) {
return webClient.get().uri("/users/{userName}/repos",userName).
exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class)).map(github->{
github.setBranch(webClient.get()
.uri("/repos/{userName}/{branchName}/branches",userName,github.getBranch())
.retrieve().bodyToFlux(Branch.class).collectList());
return github;
});
}
And WebClient is:
WebClient webClient = WebClient.builder().baseUrl("https://api.github.com").
defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json").build();
The GitHub and Branch Classes are below:
@Data
public class GithubRepo {
private String name;
private String ownerLogin;
private Mono<List<Branch>> branch;
@JsonProperty("owner")
private void unpackNested(Map<String,String> commit) {
this.ownerLogin = commit.get("login");
}
}
@Data
public class Branch {
private String name;
private Boolean protected;
}
I am getting the JSON response as:
[
{
"name": "HelloWorld",
"ownerLogin": "test",
"branch": {
"scanAvailable": true
}
},
{
"name": "rokehan",
"ownerLogin": "test",
"branch": {
"scanAvailable": true
}
},
{
"name": "sNews",
"ownerLogin": "test",
"branch": {
"scanAvailable": true
}
},
{
"name": "Test--01",
"ownerLogin": "test",
"branch": {
"scanAvailable": true
}
}
]
I want to get the response as:
[
{
"name": "HelloWorld",
"ownerLogin": "test",
"branch": [
{
"name": "asd",
"protected": false
},
{
"name": "master",
"protected": false
}
]
},
{
"name": "rokehan",
"ownerLogin": "test",
"branch": [
{
"name": "master",
"protected": false
}
]
},
{
"name": "sNews",
"ownerLogin": "test",
"branch": []
},
{
"name": "Test--01",
"ownerLogin": "test",
"branch": [
{
"name": "master",
"protected": false
}
]
}
]
Please help me to resolve this problem.
Solution
I am not sitting by a computer so i cant check against a compiler (im writing this on mobile)
But the problem is that you are trying to serialize a Mono<List<T>>
which means you are trying to send something that might not have been resolved yet. You need to return a List<T>
.
private Flux<MyResponse> getGitRepos(String userName) {
return webClient.get()
.uri("/users/{userName}/repos", userName)
.exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class))
.flatMap(github -> {
return webClient.get()
.uri("/repos/{userName}/{branchName}/branches", userName, github.getBranch())
.retrieve()
.bodyToFlux(Branch.class)
.collectList())
.flatMap(branches -> {
return Mono.just(new MyResponse(github, branches));
});
}
I wrote this free hand but you should get the point. Have one class against the github api, and another against your api so you can alter your api freely if the api against github changes.
Answered By - Toerktumlare
Answer Checked By - David Goodson (JavaFixing Volunteer)