Search code examples
javaspringreactive-programmingspring-webfluxspring-webclient

how to call WebClient methods inside Mono/Flux?


I'm new to reactive programming I call the "post" webclient method inside "mailTemplateMappinRepository.map()" in order not to interrupt the chain and pass the necessary parameters (data) to the "post" method

I'm not sure if this is a good practice and if I need to call .subscribe() for the "post" method


    return mailTemplateMappingRepository
        .findById(request.getTemplateKey())
        .switchIfEmpty(Mono.error(new MailTemplateNotSupportedException(
            "The template with key " + request.getTemplateKey() + " is not supported!!!")))
        .map(t -> {
          log.info("sendEmailWithRetry: request {}", request);
          log.info("sendEmailWithRetry: templateMappings {}", t);

          if (!businessUnitAuthTokens.containsKey(t.getExactTargetBusinessUnit())) {
            updateBusinessUnitToken(t);
          }

          String token = "Bearer " + businessUnitAuthTokens.get(t.getExactTargetBusinessUnit());
          String uri = exactTargetMessageDefinitionSendsUrl.replace("{key}", t.getExactTargetKey());
          Map<String, Object> mailTriggerPayload = generateMailTriggerPayload(request);

          RetryBackoffSpec is401RetrySpec = Retry.backoff(1, Duration.ofSeconds(2))
              .filter(throwable -> throwable instanceof Unauthorized)
              .doBeforeRetry(retrySignal -> {
                log.error("UNAUTHORIZED... WILL TRY AGAIN... Request: " + request);
                updateBusinessUnitToken(t);
              })
              .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) ->
                  new ExactTargetException(HttpStatus.UNAUTHORIZED.value(), retrySignal.failure().getMessage(),
                      "There is something wrong with the authorization key for business unit: "
                          + request.getTemplateKey())
              );

          return restClientService.post(uri, mailTriggerPayload, token, String.class)
              .onErrorResume(error -> {
                if (error instanceof ExactTargetException) {
                  return Mono.error(new ExactTargetException(((ExactTargetException) error).getStatus(),
                      ((ExactTargetException) error).getBody(),
                      "Exact Target has errors... Status: " + ((ExactTargetException) error).getStatus()
                          + " Entity: "
                          + ((ExactTargetException) error).getBody()));
                }
                return Mono.error(error);
              }).retryWhen(is401RetrySpec).subscribe();
        });
  }

Are there any examples of applications or any other information on this topic?


Solution

  • You're right about calling subscribe() in the middle of your chain. Don't do that.

    .map()  
    

    semantically assumes that you want to map your object to another object or to change something within that object. This operation is synchronous.

    However, WebClient calls return Publisher (some "promise") that can be either Mono or Flux

    That means you should call it within .flatMap() that is asynchronous.

    .flatMap()
    

    Takes an object and returns some Publisher. So you should wrap your WebClient call to .flatMap() and you don't need to subscribe inside this operator.