UserServiceClient.java
package com.dani.bff.gateway;
import com.dani.bff.config.DownstreamProperties;
import com.dani.bff.dto.DownstreamUserResponse;
import com.dani.bff.dto.UserProfile;
import com.dani.bff.error.DownstreamServiceException;
import java.time.Duration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
/**
* HTTP client for the user service profile endpoint.
*/
@Component
public class UserServiceClient {
private static final String SERVICE_NAME = "user-service";
private final WebClient webClient;
private final Duration timeout;
public UserServiceClient(
@Qualifier("userServiceWebClient") WebClient webClient,
DownstreamProperties properties) {
this.webClient = webClient;
this.timeout = properties.getUserService().getTimeout();
}
/**
* Fetches a user profile from the user service.
*
* @param userId authenticated user identifier
* @return user profile mapped to the BFF contract
*/
public Mono<UserProfile> fetchUserProfile(String userId) {
return webClient.get()
.uri("/users/{userId}", userId)
.retrieve()
.onStatus(HttpStatusCode::isError, response -> DownstreamErrorMapper.toException(response, SERVICE_NAME))
.bodyToMono(DownstreamUserResponse.class)
.map(response -> response.toUserProfile(userId))
.switchIfEmpty(Mono.error(new DownstreamServiceException(
SERVICE_NAME, HttpStatus.OK, "empty response body")))
.timeout(timeout);
}
}