Home > Net >  How to error handle for 20 reactive async API calls in java?
How to error handle for 20 reactive async API calls in java?

Time:12-07

I'm writing a service that calls 20 external vendor APIs, aggregates that data and writes it to a blob storage. This is how I am calling each api, after I am using Mono.zip() and writing that result into a blob storage. However I feel that the way I am writing the code is really redundant, specifically the error handling using .onErrorResume() and .doOnSuccess() Is there a way I can maek this code cleaner by using generics or utilizing inheritance some way? I just dont want hundreds of lines of code that are basically doing the same thing...

        Mono<MailboxProvidersDTO> mailboxProvidersDTOMono = partnerAsyncService.asyncCallPartnerApi(getMailboxProvidersUrl, MailboxProvidersDTO.class)
                .retryWhen(getRetrySpec())
                //need to have error handling for all 20 api calls
                .doOnSuccess(res -> {
                    log.info(Logger.EVENT_SUCCESS, "Mailbox Providers report successfully retrieved.");
                    res.setStatus("Success");
                })
                .onErrorResume(BusinessException.class, ex -> {
                    log.error(Logger.EVENT_FAILURE, ex.getMessage());
                    MailboxProvidersDTO audienceExplorerDTO = new MailboxProvidersDTO();
                    audienceExplorerDTO.setStatus("Failed");
                    return Mono.just(audienceExplorerDTO);
                });
        Mono<TimeZonesDTO> timeZonesDTOMono = partnerAsyncService.asyncCallPartnerApi(getTimeZonesUrl, TimeZonesDTO.class);
        Mono<RegionsDTO> regionsDTOMono = partnerAsyncService.asyncCallPartnerApi(getRegionsUrl, RegionsDTO.class);
        Mono<AudienceExplorerDataSourcesDTO> audienceExplorerDataSourcesDTOMono = partnerAsyncService.asyncCallPartnerApi(getAudienceExplorerDataSourcesUrl, AudienceExplorerDataSourcesDTO.class);

...

CodePudding user response:

You can effectively use generics to refactor your code. You can couple functional interfaces and Generics to create what you need:

  1. On your example, you need both to "setStatus" and create new instances of different classes. You could then create a utility function to add onSuccess/onFailure behaviours over your initial data fetching Mono:
public <T> Mono<T> withRecovery(Mono<T> fetchData, BiConsumer<T, String> setStatus, Supplier<T> createFallbackDto) {
    return fetchData
               .doOnSuccess(result -> {
                   log.info...
                   setStatus.accept(result, "Success");
               })
               .doOnError(BusinessException.class, err -> {
                   log.error...
                   T fallback = createFallbackDto.get();
                   setStatus.accept(fallback, "Error");
                   return Mono.just(fallback);
               });
}
  1. Then, you can use this method like that:
Mono<MailProvidersDto> mails = withRecovery(
    partnersAsyncService.asyncCallPartnerApi(getMailboxProvidersUrl, MailboxProvidersDTO.class),
    MailProvidersDto::setStatus,
    MailProvidersDto::new
);

Mono<TimeZoneDto> timezone = withRecoery(
    partnersAsyncService.asyncCallPartnerApi(getMailboxProvidersUrl, TimeZoneDto.class),
    TimeZoneDto::setStatus,
    TimeZoneDto::new
);

... // Repeat for each api

Notes:

  1. If the setStatus method is available through a common interface that all DTO implement, you can get rid of the biconsumer, and directly call result.setStatus(String), by specializing T generic to T extends StatusInterface.
  2. With it, you could also factorize initial fetching and retry calls, by passing related parameters (url, class, retry spec) as method input.
  • Related