Skip to content
Advertisement

How make sequential API calls and merge the result in a mono

I try to make sequential API calls and merge the result in a mono . The first API contains a List of Id for the second API.

    @Service
    @AllArgsConstructor
    public class AggregateService {
    
        private  OperationClient operationClient;
        private  OuvrageClient ouvrageClient;
    
        public Mono<OperationDetailDto> getDetails(String operationId){
    
            final Mono<OperationFullDto> operationMono = operationClient.getOperation(operationId);
            return operationMono
                    .flatMap(operation -> {
                            final OperationDetailDto detail = new OperationDetailDto();
                            final Mono<List<OuvrageDto>> ouvrageMono = ouvrageClient.getOuvrages(operation.getOuvrageIds());
                            Mono<Tuple2<OperationFullDto, List<OuvrageDto>>> tuple2 = Mono.zip(operationMono,ouvrageMono);
                                tuple2.map(result ->{
                                    detail.setId(ServiceUtils.formatUuid(operationId));
                                    detail.setTitre(result.getT1().getTitre());
                                    detail.setOuvrages(result.getT2());
                                    return detail;
                                });
                                return Mono.just(detail);
                                }
                            );
        }
    
    }

My functional WebClient

    @Service
    public class OperationClient {
    
        private final WebClient webClient;
    
        public OperationClient(WebClient.Builder builder) {
            this.webClient = builder.baseUrl("lb://operation-microservice/operation/").build();
        }
    
        public Mono<OperationFullDto> getOperation(String operationId){
            return webClient
                    .get()
                    .uri("{id}" , operationId)
                    .retrieve()
                    .bodyToMono(OperationFullDto.class)
                    .subscribeOn(Schedulers.parallel())
                    .log()
                    .onErrorResume(ex->Mono.empty());
        }
    }

The ouvrageClient was invoked but I don’t see log

public class OuvrageClient {
    private final WebClient webClient;

    public OuvrageClient(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("lb://ouvrage-microservice/ouvrage/").build();
    }

    public Mono<List<OuvrageDto>> getOuvrages(List<Long> ids){
        System.out.println("the void getOuvrages was invoked " + ids );
        return webClient
                .get()
                .uri("multi/{ids}",ids)
                .retrieve()
                .bodyToFlux(OuvrageDto.class)
                .subscribeOn(Schedulers.parallel())
                .collectList()
                .onErrorReturn(Collections.emptyList())
                .log();
    }
}

Log

2022-06-21 10:54:37.872  INFO 252 --- [ctor-http-nio-3] reactor.Mono.SubscribeOn.1               : onSubscribe(MonoSubscribeOn.SubscribeOnSubscriber)
2022-06-21 10:54:37.875  INFO 252 --- [ctor-http-nio-3] reactor.Mono.SubscribeOn.1               : request(unbounded)
2022-06-21 10:54:38.724  INFO 252 --- [ctor-http-nio-1] reactor.Mono.SubscribeOn.1               : onNext(OperationFullDto(id=c4f62ea6-0387-4021-b5de-d056105612ce,  titre=test3,  ouvrageIds=[2]))
the void getOuvrages was invoked [2]
2022-06-21 10:54:38.796  INFO 252 --- [ctor-http-nio-1] reactor.Mono.SubscribeOn.1               : onComplete()

The ouvrageClient was invoked but I don’t see log for the ouvrageClient …. and the global result of my AggregateService is empty

{"id": null,"titre": null,"ouvrages": null}

Thanks


Thanks for the answer ! what do you think of this piece of code?

    public Mono<OperationDetailDto> getDetails(String operationId){

        final Mono<OperationFullDto> operationMono = operationClient.getOperation(operationId);
        return operationMono.zipWhen( operation  -> ouvrageClient.getOuvrages(operation.getOuvrageIds()) )
                .map( tuple2  -> {
                    OperationDetailDto detail = new OperationDetailDto();
                    detail.setId(ServiceUtils.formatUuid(operationId));
                    detail.setTitre(tuple2.getT1().getTitre());
                    detail.setOuvrages(tuple2.getT2());
                    return detail;
                });

    }

Advertisement

Answer

You could return the tuple2.map to the flatMap operation directly, but make sure you create and return the OperationDetailDto inside the map‘s lambda.

.flatMap(operation -> {
    final Mono<List<OuvrageDto>> ouvrageMono = 
        ouvrageClient.getOuvrages(operation.getOuvrageIds());
    Mono<Tuple2<OperationFullDto, List<OuvrageDto>>> tuple2 =
        Mono.zip(operationMono, ouvrageMono);

    return tuple2.map(result -> {
        final OperationDetailDto detail = new OperationDetailDto();
        detail.setId(ServiceUtils.formatUuid(operationId));
        detail.setTitre(result.getT1().getTitre());
        detail.setOuvrages(result.getT2());
        return detail;
    });
})
Advertisement