Search code examples
javareactive-programminghelidonhelidon-webclient

convert for loop in java reactive programming - helidon


I am new to reactive programming and using helidon reactive java libraries in our code. I am unable to achieve the below use case.

I have a scenario as below.

First I invoke a REST API and get response.From the response that contains list of countries I have to invoke another REST api that retrieves the response for a country id and update the country object.

By the time I invoke second API and set value to country object as below the response is already returned. I get use .get() and wait() on Single as it blocks the thread.

Please suggest how to overcome the below for loop and update the list of objects reactive way.

Single<WebClientResponse> singleWebClientResp = webClient.get("REST_URL");

Single<String> apiResponse = singleWebClientResponse.flatMapSingle(webClientResponse -> {
        return webClientResponse.content().as(String.class);
});

apiResponse.flatMapSingle(fusionAPIResponseString -> {

    List<Country> countries = 
        objectMapper.readValue(fusionAPIResponseString,new TypeReference<List<Country>>() {});
        
    for (Country country : countries) {
        getCountryByRegion(country.getRegion()).forSingle(newCountry -> {

            LOGGER.log(Level.FINE, "newCountry ---> " + newCountry);

            country.setRegion(country.getRegion() + "modified" + newCountry);

        });
    }
        
});
        
private Single<String> getCountryByRegion(String regionName) {
    LOGGER.log(Level.FINE, "Entering getCountryByRegion");

    Single<WebClientResponse> singleWebClientResponse2 = webClient.get().path("v3.1/region/" + regionName)
            .contentType(MediaType.APPLICATION_JSON).request();

    Single<String> retVal = singleWebClientResponse2.flatMapSingle(webClientResponse -> {
        return webClientResponse.content().as(String.class);
    });

    LOGGER.log(Level.FINE, "Exiting getCountryByRegion");
    return retVal;

}

Regards


Solution

  • // NOTE: this should be a static constant
    GenericType<List<Country>> countriesType = new GenericType<>() {};
    
    
    // NOTE: create the webClient only once, not for every request
    WebClient webClient = WebClient.builder()
                                   .addMediaSupport(JacksonSupport.create())
                                   .baseUri("service-url")
                                   .build();
    
    // the pipeline starts with the initial countries (i.e. Single<List<Country>>)
    webClient.get()
            .path("/countries")
            // get the countries as List<Country>
            .request(countriesType)
    
            // add each country to the reactive pipeline (i.e. Multi<Country>)
            // to allow individual reactive mapping
            .flatMap(Multi::just)
    
            // map each country by creating a new country with new region
            // use flatMap to inline the webClient result in the reactive pipeline
            .flatMap(country ->
                    webClient.get()
                             .path("/region/" + country.getRegion())
                             .request(String.class)
                             .map(newCountry -> new Country(newCountry, country.getRegion())))
    
            // aggregate all items (i.e. Single<List<Country>>)
            .collectList()
            .onError(res::send)
            .forSingle(res::send))