package eu.codearte.resteeth.core;
import eu.codearte.resteeth.endpoint.EndpointProvider;
import eu.codearte.resteeth.handlers.RestInvocationHandler;
import eu.codearte.resteeth.util.SpringUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.lang.reflect.Field;
import java.util.*;
/**
* @author Jakub Kubrynski
*/
class RestTemplateInvoker implements RestInvocationHandler {
private final RestTemplate restTemplate;
private final EndpointProvider endpointProvider;
RestTemplateInvoker(RestTemplate restTemplate, EndpointProvider endpointProvider) {
this.restTemplate = restTemplate;
this.endpointProvider = endpointProvider;
}
@Override
public Object proceed(RestInvocation invocation) {
MethodMetadata methodMetadata = invocation.getMetadata();
Map<String, ?> urlVariablesValues = buildArgumentsMap(methodMetadata.getParameterMetadata().getUrlVariables(), invocation.getArguments());
String requestUrl = endpointProvider.getEndpoint() + methodMetadata.getMethodUrl();
requestUrl = appendAnnotatedQueryParameters(requestUrl, methodMetadata.getParameterMetadata().getQueryParameters(), invocation.getArguments());
requestUrl = appendPojoQueryParameters(requestUrl, methodMetadata.getParameterMetadata().getPojoQueryIndex(), invocation.getArguments());
HttpHeaders headers = new HttpHeaders();
headers.putAll(methodMetadata.getHttpHeaders());
headers.putAll(invocation.getDynamicHeaders());
@SuppressWarnings("unchecked")
HttpEntity entity = new HttpEntity(
extractRequestBody(methodMetadata.getParameterMetadata().getRequestBodyIndex(), invocation.getArguments()),
headers);
Class responseType;
boolean returnsResponseEntity = ResponseEntity.class.isAssignableFrom(methodMetadata.getReturnType());
if (returnsResponseEntity) {
responseType = SpringUtils.getGenericType(invocation.getMethod().getGenericReturnType());
} else {
responseType = methodMetadata.getReturnType();
}
@SuppressWarnings("unchecked")
ResponseEntity<?> exchange = restTemplate.exchange(requestUrl, methodMetadata.getRequestMethod(), entity,
responseType, urlVariablesValues);
return returnsResponseEntity ? exchange : exchange.getBody();
}
private Object extractRequestBody(Integer requestBody, Object[] arguments) {
if (requestBody != null && arguments != null && arguments.length >= requestBody) {
return arguments[requestBody];
}
return null;
}
private Map<String, ?> buildArgumentsMap(Map<Integer, String> urlVariables, Object[] arguments) {
Map<String, Object> stringHashMap = new HashMap<>();
for (Integer paramIndex : urlVariables.keySet()) {
stringHashMap.put(urlVariables.get(paramIndex), arguments[paramIndex]);
}
return stringHashMap;
}
private String appendPojoQueryParameters(String requestUrl, Integer pojoQueryParameter, Object[] arguments) {
if (pojoQueryParameter == null) {
return requestUrl;
}
Map<String, Object> queryParamsMap = new HashMap<>();
Object pojoParameterValue = arguments[pojoQueryParameter];
List<Field> fields = getAllFields(pojoParameterValue.getClass());
for (Field field : fields) {
if (!field.isSynthetic()) {
try {
String property = BeanUtils.getProperty(pojoParameterValue, field.getName());
if (property != null) {
queryParamsMap.put(field.getName(), property);
}
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Cannot do reflection magic on " + pojoParameterValue.getClass(), e);
}
}
}
return appendQueryParameters(requestUrl, queryParamsMap);
}
private static List<Field> getAllFields(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
private String appendAnnotatedQueryParameters(String requestUrl, Map<Integer, String> queryParameters, Object[] arguments) {
return appendQueryParameters(requestUrl, buildArgumentsMap(queryParameters, arguments));
}
private String appendQueryParameters(String requestUrl, Map<String, ?> queryParamsMap) {
if (queryParamsMap.isEmpty()) {
return requestUrl;
}
StringBuilder urlBuilder = new StringBuilder(requestUrl);
if (urlBuilder.indexOf("?") == -1) {
urlBuilder.append("?");
}
boolean isFirst = true;
String[] keys = queryParamsMap.keySet().toArray(new String[queryParamsMap.size()]);
Arrays.sort(keys);
for (String paramName : keys) {
if (isFirst) {
isFirst = false;
} else {
urlBuilder.append("&");
}
urlBuilder.append(paramName).append("=").append(queryParamsMap.get(paramName));
}
return urlBuilder.toString();
}
@Override
public String toString() {
return "RestTemplateInvoker(" + "restTemplate=" + restTemplate +
", endpointProvider=" + endpointProvider + ')';
}
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}