I’m using WebFlux and WebClient and I need to consume two APIs and merge its responses.
The first API receive type and document number and returns a list with one element that contains customer data (that’s how it’s defined).
The second API receive a client id and returns a customer payments list.
I need to consume these two APIs and return an entity with the customer data and their payments.
API Customer response
public class CustomerResponseApi { private List<CustomerApi> clientList; }
public class CustomerApi { private int customerId; private String documentNumber; private String documentType; private String firstName; private String lastName; }
API Payment Response
public class PaymentResponseApi { private int customerId; private LocalDate paymentDate; private float amount; private String paymentType; }
finally I should have this
CustomerResponse.java
public class CustomerResponse { private int customerId; private String documentNumber; private String documentType; private String firstName; private String lastName; private List<PaymentResponseApi> payments; }
I have a proxy class that is responsible for making the API call
CustomerProxy.java
public class CustomerProxy { @Value("${api.base-url}") private String baseUrl; public Mono<CustomerResponseApi> getCustomer(String documentType, String documentNumber) { log.info("baseUrl: {}", baseUrl); WebClient webClient = WebClient.create(baseUrl); return webClient.get() .uri(uri -> uri .path("/customers") .queryParam("documentNumber", documentNumber) .queryParam("documentType", documentType) .build() ) .retrieve() .bodyToMono(CustomerResponseApi.class); } }
PaymentProxy.java
public class PaymentProxy { @Value("${api.base-url}") private String baseUrl; public Flux<PaymentResponseApi> getCustomerPayment(int customerId) { log.info("baseUrl: {}", baseUrl); WebClient webClient = WebClient.create(baseUrl); return webClient.get() .uri(uri -> uri .path("/payments") .queryParam("customerId", customerId) .build() ) .retrieve() .bodyToFlux(PaymentResponseApi.class); } }
And a service that is responsible for merge responses CustomerServiceImpl.java
public class CustomerServiceImpl implements CustomerService { @Autowired private CustomerProxy customerProxy; @Autowired private PaymentProxy paymentProxy; @Override public Mono<CustomerResponse> getCustomerAndPayments(String documentType, String documentNumber) { return customerProxy.getCustomer(documentType, documentNumber).flatMap(resp -> { CustomerApi customerApi = resp.getClientList().get(0); //always returns one customer // Here is my problem, because getCustomerPayment method returns a Flux List<PaymentResponseApi> payments = paymentProxy.getCustomerPayment(customerApi.getCustomerId()); CustomerResponseBuilder customerBuilder = CustomerResponse.builder() .customerId(customerApi.getCustomerId()) .documentNumber(customerApi.getDocumentNumber()) .documentType(customerApi.getDocumentType()) .firstName(customerApi.getFirstName()) .lastName(customerApi.getLastName()) .payments(payments); return Mono.just(customerBuilder.build()); }); } }
Two ways to resolve this:
public Mono<CustomerResponse> getCustomerAndPayments(String documentType, String documentNumber) { return customerProxy.getCustomer(documentType, documentNumber) .map(resp -> resp.getClientList().get(0)) .flatMap(customerApi -> { Flux<PaymentResponseApi> paymentProxyFlux = paymentProxy.getCustomerPayment(customerApi.getCustomerId()); return paymentProxyFlux.collectList() .map(payments -> { CustomerResponseBuilder customerBuilder = CustomerResponse.builder() .customerId(customerApi.getCustomerId()) .documentNumber(customerApi.getDocumentNumber()) .documentType(customerApi.getDocumentType()) .firstName(customerApi.getFirstName()) .lastName(customerApi.getLastName()) .payments(payments); return customerBuilder.build(); }); }); }
Thus basically using Method2 you need this:
public Mono<CustomerResponse> getCustomerAndPayments(String documentType, String documentNumber) { Mono<CustomerApi> customerApiMono = customerProxy.getCustomer(documentType, documentNumber) .map(resp -> resp.getClientList().get(0)); Mono<List<PaymentResponseApi>> paymentResponseApiListMono = customerApiMono .flatMapMany(customerApi -> paymentProxy.getCustomerPayment(customerApi.getCustomerId())) .collectList(); return customerApiMono.zipWith(paymentResponseApiListMono) .map(tuple -> { CustomerApi customerApi = tuple.getT1(); List<PaymentResponseApi> payments = tuple.getT2(); CustomerResponseBuilder customerBuilder = CustomerResponse.builder() .customerId(customerApi.getCustomerId()) .documentNumber(customerApi.getDocumentNumber()) .documentType(customerApi.getDocumentType()) .firstName(customerApi.getFirstName()) .lastName(customerApi.getLastName()) .payments(payments); return customerBuilder.build(); }); }
Demerit of Method2: Api1 i.e customer API will be be subscribed twice.
Recent Comments