HttpIdentityClient.java
package com.ecommerce.trade.infrastructure.client;
import com.ecommerce.platform.common.api.ApiResponse;
import com.ecommerce.trade.application.exception.TradeError;
import com.ecommerce.trade.application.exception.TradeException;
import com.ecommerce.trade.application.port.AddressPort;
import com.ecommerce.trade.infrastructure.config.InternalClientProperties;
import com.ecommerce.trade.infrastructure.config.RemoteClientProperties;
import com.ecommerce.trade.infrastructure.resilience.RemoteDependencyFailure;
import com.ecommerce.trade.infrastructure.resilience.TradeSynchronousBoundaryResilience;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestClientResponseException;
import java.util.Objects;
@Component
public class HttpIdentityClient implements AddressPort {
private static final ParameterizedTypeReference<ApiResponse<AddressResponse>> RESPONSE_TYPE =
new ParameterizedTypeReference<>() {
};
private final RestClient restClient;
private final InternalClientProperties properties;
private final TradeSynchronousBoundaryResilience resilience;
@Autowired
public HttpIdentityClient(
RestClient.Builder tradeRestClientBuilder,
InternalClientProperties properties,
RemoteClientProperties clientProperties,
TradeSynchronousBoundaryResilience resilience) {
this(
tradeRestClientBuilder.baseUrl(clientProperties.identityBaseUrl()).build(),
properties,
resilience);
}
HttpIdentityClient(
RestClient restClient,
InternalClientProperties properties,
TradeSynchronousBoundaryResilience resilience) {
this.restClient = restClient;
this.properties = properties;
this.resilience = resilience;
}
@Override
public AddressSnapshot getAddress(Long userId, Long addressId) {
return resilience.execute(
TradeSynchronousBoundaryResilience.Boundary.IDENTITY_QUERY,
() -> requestAddress(userId, addressId));
}
private AddressSnapshot requestAddress(Long userId, Long addressId) {
try {
ApiResponse<AddressResponse> response = restClient.get()
.uri("/api/v1/identity/internal/users/{userId}/addresses/{addressId}", userId, addressId)
.header("X-Internal-Service", properties.caller())
.header("X-Internal-Token", properties.token())
.retrieve().body(RESPONSE_TYPE);
if (response == null || response.data() == null) {
throw RemoteDependencyFailure.invalidResponse();
}
AddressResponse address = response.data();
if (!Objects.equals(addressId, address.id())) {
throw RemoteDependencyFailure.invalidResponse();
}
return new AddressSnapshot(address.id(), address.recipientName(), address.phone(),
address.province(), address.provinceCode(), address.city(), address.cityCode(),
address.district(), address.districtCode(), address.detailAddress(), address.postalCode());
} catch (RestClientResponseException exception) {
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new TradeException(TradeError.ADDRESS_UNAVAILABLE, exception);
}
throw RemoteDependencyFailure.forHttpStatus(exception.getStatusCode(), exception);
} catch (TradeException exception) {
throw exception;
} catch (RemoteDependencyFailure exception) {
throw exception;
} catch (ResourceAccessException exception) {
throw RemoteDependencyFailure.transientFailure(exception);
} catch (RestClientException exception) {
throw RemoteDependencyFailure.invalidResponse(exception);
} catch (RuntimeException exception) {
throw RemoteDependencyFailure.invalidResponse(exception);
}
}
private record AddressResponse(
Long id,
String recipientName,
String phone,
String province,
String provinceCode,
String city,
String cityCode,
String district,
String districtCode,
String detailAddress,
String postalCode
) {
}
}