2

I'm new to reactive programming and WebClient. I would like to repeatedly call an API until I get a specific value from the API (the API I am calling is a regular API that's configured for event streaming). Once I get the desired value, I would take an action.

I attempted to solve this with Flux interval, but haven't had much luck. I'm not sure if there is something wrong with my code, or if I am taking the wrong approach.

public Flux<TransactionStatus> watchAndTransact(Duration intervalDuration, Duration watchDuration,
                                                String toAddress, String fromAddress) {
    return Flux.interval(intervalDuration)
            .take(watchDuration)
            .flatMap(aFloat -> getBalance(fromAddress))
            //.retryWhen(getBalance(fromAddress).block() < 0)
            .flatMap(balance -> {

                log.info("Checking balance {}", Instant.now());
                //If we find that an amount was deposited into the deposit account
                if (balance > 0) {
                    log.info("Balance Present {}", balance.toString());

                    //Build a transaction
                    Transaction transaction = new Transaction(fromAddress, toAddress, balance);

                    //Post the transaction
                    return postTransactionService.postTransaction(transaction);

                }
                return Mono.empty();
            }).switchIfEmpty(s -> s.onError(new RuntimeException()));
}

I am getting the balance from the API using the code below:

public Mono<AddressInfo> getAddressInfo(String address) {
    log.info("Pulling information on the address {}", address);

    return webConfig.api
            .get()
            .uri("/addresses/{address}", address)
            .retrieve()
                .onStatus(HttpStatus::is4xxClientError,
                        error -> Mono.error(new NotFoundResourceException("Couldn't find a record")))
                .onStatus(HttpStatus::is5xxServerError,
                        error -> Mono.error(new BadRequestException("Server isn't responding")))
            .bodyToMono(AddressInfo.class)
            .onErrorReturn(new AddressInfo(0f, EMPTY_LIST));
}

public Mono<Float> getBalance(String address) {
    return getAddressInfo(address).map(AddressInfo::getBalance);
}

This is how I setup my webclient:

@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {

    return WebClient.builder();
}

public WebClient dbClient = loadBalancedWebClientBuilder()
        .baseUrl(gatewayUrl+"/api/v2/data")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();

public WebClient apiClient = loadBalancedWebClientBuilder()
        .baseUrl(url)
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();

How Can I call a regular API repeatedly using Spring Boot Reactive?

Mabel Oza
  • 557
  • 8
  • 22
  • 1
    Have you tried using [repeatWhen](https://stackoverflow.com/questions/61611207/java-loop-until-condition-for-webclient-returning-mono) – Superchamp Jul 30 '21 at 02:14

1 Answers1

2

Didn't find a perfect solution to this problem, but found something close enough.

The method below can call the API multiple times and takes a certain action after it finds out the balance is greater than 0. It makes use of Flux.repeat and a BooleanSupplier.

The problem is it will keep calling the API even after it finds a balance greater than 0. Another problem, related to the first one, is that at the end of the run it throws a service unavailable exception. This is probably related to the fact that this code can't stop after finding a balance greater than 0.

/**
 * Check network consistently to see that the transaction went through
 *
 * This method gives us the amount that's deposited to the deposit account
 * and posts a transaction from the deposit account to the house account.
 *
 * @param intervalDuration
 * @param watchDuration
 * @param toAddress
 * @param fromAddress
 * @return
 */
//TODO: Fix this method, after it finds the balance and
// posts the transactio it keeps checking for the balance
//TODO: Handle Service Unavailable Exception thrown when
// the watch duration expires
public Flux<TransactionStatus> watchAndTransact(
        Duration intervalDuration, Duration watchDuration,
        String toAddress, String fromAddress) {

    log.info("Checking balance {}", Instant.now());

    //Boolean that determines how long we repeat this interval
    BooleanSupplier isBalanceAbove0 = () -> {
        Float balance = networkWatchService.getBalance(fromAddress).block();
        if (balance > 0) {
            log.info("Balance Present {}", balance.toString());
            return true;
        }
        return false;
    };

    return Flux.interval(intervalDuration)
        .take(watchDuration)
        .flatMap(aFloat -> networkWatchService.getBalance(fromAddress))
        .flatMap(balance -> {
            log.info("Checking balance {}", Instant.now());
            //If we find that an amount was deposited into the deposit account
            if (balance > 0) {
                log.info("Balance Present {}", balance.toString());
                //Build a transaction
                Transaction transaction = 
                        new Transaction(fromAddress, toAddress, balance);
                //Post the transaction
                return postTransactionService.postTransaction(transaction);
            }
            return Mono.empty();
        })
        .repeat(isBalanceAbove0)
        .switchIfEmpty(s -> s.onError(new Exception("Balance insufficient")));
}
Mabel Oza
  • 557
  • 8
  • 22