Two Dependent web client api calls in spring boot

I have two api calls from I need to get second api call result based on first api call result after that I need to merger both api call responses let me write here

  1. Api call : https://localhost:8080/projects this is the first api call this result is like below

    [ {

     "projectId" : 2,
     "projectName" : "Hello",
     "projectDesc" : "Demo project"
     },
     {
    
     "projectId" : 3,
     "projectName" : "Hello123",
     "projectDesc" : "Demo project series"
     },
     {
        "projectId" : 4,
         "projectName" : "Hello456",
         "projectDesc" : "Demo project repeat"
         }
     ]
    
  2. Second api call http://localhost:8080/projectId in the sense (http://localhost:8080/2) this result as below

[

{
"teamMember" : "abc",
"teamMemberRole": "Manager"
}
]

Now I need to merge both responses , How I can achieve this using webclient

[
{

"projectId" : 2,
"projectName" : "Hello",
"projectDesc" : "Demo project",
"teamMember" : "abc",
"teamMemberRole" : "Manager"
}
]

Your help is appreciate. Thanks in advance

Upvotes: 1

Views: 1749

Answers (1)

Harry
Harry

Reputation: 508

I think i understand your problem and its pretty common. The key thing which you would like to do is to create a flux of responses where each element is merged with Flux of child response. ie.

  • Get the Projects
    • For each project
      • pass the projectId and get Team members
    • Merge/Map each project with team members
  • Return flux of Projects with added data of team members

Assuming that you have a service class which can get the data for projects and team members for a given project.

getAllProjects getTeamMembers

You can use this to write the resulting function called "getProjectsWithTeamMembers"

@Component
@Slf4j
public class ProjectHandler {

    @Autowired
    private WebClient webClient;

    public Flux<Project> getAllProjects() {

        return webClient
            .get()
            .uri("projects")
            .retrieve()
            .bodyToFlux(Project.class);
    }

    public Mono<List<TeamMember>> getTeamMembers(Integer id) {
         ParameterizedTypeReference<List<TeamMember>> listParameterizedTypeReference =
            new ParameterizedTypeReference<List<TeamMember>>() {
            };
        return webClient.get()
                .uri("" + id)
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                    Mono.empty()
                )
                .bodyToMono(listParameterizedTypeReference)
                .log();
    }

    public Flux<Project> getProjectsWithTeamMembers() {
        return getAllProjects()
                .flatMap(project ->
                        Mono.zip(Mono.just(project),
                            getTeamMembers(project.getProjectId()).defaultIfEmpty(new ArrayList<TeamMember>()))
                .map(tuple -> {
                    log.info("data" + tuple.getT2().size());
                    return 
 Project.builder().projectId(tuple.getT1().getProjectId())
                        .projectName(tuple.getT1().getProjectName())
                        .projectDesc(tuple.getT1().getProjectDesc())
                        .teamMemberList(tuple.getT2()).build();
            }));

    }
}

Please note tuple is of

Tuple<Project,List<TeamMember>>

Here what exactly you are doing is that using Mono.zip and passing two things to it

  • Project
  • List of Team Members

Mono.zip will combine it and create Tuple2 of Project and Team members which you can use to write your mapping code to build the full fledged response.

Hope this helps.

-- Update -- Below is updated code

Config for webclient

@Configuration
public class AppConfig {

    @Bean
    public WebClient webClient(WebClient.Builder webClientBuilder) {
        return webClientBuilder
                .baseUrl("http://localhost:3000/")
                .defaultHeader(HttpHeaders.CONTENT_TYPE,  "application/json")
                .build();
    }
}

Model classes

 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 @Builder
 public class Project {
    private Integer projectId;
    private String projectName;
    private String projectDesc;
    private List<TeamMember> teamMemberList;

 }

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TeamMember {
    private String teamMember;
    private String teamMemberRole;
}

And finally the rest controller

@RestController
@Slf4j
public class ProjectController {

    @Autowired
    private ProjectHandler projectHandler;

    @GetMapping(PROJECT_ENDPOINT_V1)
    public Flux<Project> getAllProjects() {
        return projectHandler.getProjectsWithTeamMembers();
    }

}

Upvotes: 1

Related Questions