package com.uip.tatar.api;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.GsonBuilder;
import com.uip.tatar.APITatarException;
import com.uip.tatar.config.Configuration;
import com.uip.tatar.network.HttpClient;
import org.apache.http.StatusLine;
import org.json.JSONException;
import org.json.JSONObject;
import static com.uip.tatar.util.StringUtils.toSHA1;
/**
* Author: Khamidullin Kamil
* Date: 24.12.13
* Time: 11:45
*/
public class APITatarDefaultClient {
private final int[] mValidHttpStatuses = {200, 201, 204};
private final HttpClient mHttpClient;
private String mAPIVersion;
private String mFullAPITatarUrl;
private String mAuthorizationToken;
private String mSecretKey;
protected String ERROR_SERVER_DOES_NOT_RESPOND;
protected String ERROR_WRONG_SERVER_RESPONSE;
protected String mRequestUrl;
protected String mResponse;
private boolean isDebug = false;
private static final String LOG_TAG = "api_client";
private ErrorHandler errorHandler;
//private final Logger LOGGER = Logger.makeLogger(LOG_TAG);
public APITatarDefaultClient(Configuration configuration) {
setConfig(configuration);
setErrorHandler(new DefaultErrorHandler());
mHttpClient = new HttpClient();
}
private void initConfig(Configuration configuration) {
mAPIVersion = configuration.getVersion();
mFullAPITatarUrl = String.format("%s://%s/api/%s",
configuration.isEnableSSL()? "http" : "https",
configuration.getHost(),
configuration.getVersion()
);
mAuthorizationToken = configuration.getAuthorizationToken();
mSecretKey = configuration.getSecretKey();
ERROR_SERVER_DOES_NOT_RESPOND = configuration.getServerDoesNotRespondDefaultErrorText();
ERROR_WRONG_SERVER_RESPONSE = configuration.getWrongServerResponseDefaultErrorText();
isDebug = configuration.isDebug();
}
public void setConfig(Configuration configuration) {
initConfig(configuration);
}
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Генерирует подпись для запроса
*
* GET, DELETE = sha1(sha1(secret) + 'resource uri' + '&' + 'entity');
* POST, PUT = sha1(sha1(secret) + 'resource uri' + '&');
* @param request
* @return
*/
private String generateSignatureForRequest(APITatarRequest request) {
APITatarSupportHttpMethod httpMethod = request.getHttpMethod();
StringBuilder signatureBuilder = new StringBuilder();
signatureBuilder.append(toSHA1(mSecretKey));
signatureBuilder.append(String.format("/api/%s/%s", mAPIVersion, request.getResourceURIWithQueryString()));
signatureBuilder.append("&");
if(httpMethod == APITatarSupportHttpMethod.POST || httpMethod == APITatarSupportHttpMethod.PUT) {
signatureBuilder.append(request.getContent().toString());
}
return toSHA1(signatureBuilder.toString());
}
/**
* Проверяет код полученного HTTP статуса
*
* @param statusLine
* @return
*/
private boolean checkHttpStatus(StatusLine statusLine) {
for (int status : mValidHttpStatuses) {
if (statusLine.getStatusCode() == status)
return true;
}
return false;
}
/**
* @param request
* @param resultHandler
* @param <T>
* @return
* @throws APITatarException
*/
public <T> T get(APITatarRequest request, ResultHandler<T> resultHandler) throws APITatarException {
request.setHttpMethod(APITatarSupportHttpMethod.GET);
return send(request, resultHandler, errorHandler);
}
/**
*
* @param request
* @param resultHandler
* @param <T>
* @return
* @throws APITatarException
*/
public <T> T post(APITatarRequest request, ResultHandler<T> resultHandler) throws APITatarException {
request.setHttpMethod(APITatarSupportHttpMethod.POST);
return send(request, resultHandler, errorHandler);
}
/**
*
* @param request
* @param resultHandler
* @param <T>
* @return
* @throws APITatarException
*/
public <T> T put(APITatarRequest request, ResultHandler<T> resultHandler) throws APITatarException {
request.setHttpMethod(APITatarSupportHttpMethod.PUT);
return send(request, resultHandler, errorHandler);
}
/**
*
* @param request
* @param resultHandler
* @param <T>
* @return
* @throws APITatarException
*/
public <T> T delete(APITatarRequest request, ResultHandler<T> resultHandler) throws APITatarException {
request.setHttpMethod(APITatarSupportHttpMethod.DELETE);
return send(request, resultHandler, errorHandler);
}
protected void onPrepareRequest(APITatarRequest request) {
request.setApiUrl(mFullAPITatarUrl);
request.addHeader(APITatarConstants.AUTH_TOKEN_HEADER, mAuthorizationToken);
request.addHeader("Accept-Encoding", "gzip");
}
/**
*
* @param request
* @param handler
* @param errorHandler
* @param <T>
* @return
* @throws APITatarException
*/
protected <T> T send(APITatarRequest request, ResultHandler<T> handler, ErrorHandler errorHandler) throws APITatarException {
onPrepareRequest(request);
//generate request signature and sign request
request.sign(generateSignatureForRequest(request));
mRequestUrl = request.buildFullResourceUrl();
if(isDebug) {
System.out.println(request);
}
switch (request.getHttpMethod()) {
case GET:
mHttpClient.get(mRequestUrl, request.getHeaders());
break;
case DELETE:
mHttpClient.delete(mRequestUrl, request.getHeaders());
break;
case POST:
mHttpClient.post(mRequestUrl, request.getContent(), request.getHeaders());
break;
case PUT:
mHttpClient.put(mRequestUrl, request.getContent(), request.getHeaders());
break;
default:
throw new APITatarException("Unsupported HTTP method");
}
final APITatarResponse response = new APITatarResponse(mHttpClient.getResponse());
if(isDebug) {
System.out.println(response);
}
if(checkHttpStatus(mHttpClient.getResponseStatus())) {
try {
return (handler != null) ? handler.handleResult(response) : null;
} catch (Exception jex) {
throw new APITatarException(ERROR_WRONG_SERVER_RESPONSE);
}
} else {
try {
throw errorHandler.createException(response);
} catch (JSONException jsonexception) {
throw new APITatarException(ERROR_WRONG_SERVER_RESPONSE);
}
}
}
/***
*
*/
public interface ErrorHandler {
public APITatarException createException(APITatarResponse response);
}
public static class DefaultResultHandler<Result> implements ResultHandler<Result> {
private Class<Result> cls;
private boolean withUnderscores;
public DefaultResultHandler(Class<Result> cls) {
this.cls = cls;
this.withUnderscores = false;
}
public DefaultResultHandler(Class<Result> cls, boolean withUnderscores) {
this.cls = cls;
this.withUnderscores = withUnderscores;
}
@Override
public Result handleResult(APITatarResponse response) throws Exception {
return new GsonBuilder().setFieldNamingPolicy((withUnderscores) ? FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES : FieldNamingPolicy.IDENTITY)
.create()
.fromJson(response.getData(), cls);
}
}
public static class DefaultErrorHandler implements ErrorHandler {
@Override
public APITatarException createException(APITatarResponse response) {
try {
JSONObject jsonResponse = new JSONObject(response.getData());
if(jsonResponse.has("message")) {
if(jsonResponse.getString("message").equals("Invalid username or password")) {
return new APITatarWrongCredentialsException();
}
return new APITatarException(jsonResponse.getString("message"));
} else if(jsonResponse.has("error") && jsonResponse.getJSONObject("error").has("type") && jsonResponse.getJSONObject("error").getString("type").equals("api_user_authorization_failed")) {
return new APITatarUnauthorizationException();
} else if(jsonResponse.has("error") && jsonResponse.getJSONObject("error").has("message")) {
return new APITatarException(jsonResponse.getJSONObject("error").getString("message"));
} else if(jsonResponse.has("status") && jsonResponse.getString("status").equals("Unauthorized")) {
return new APITatarUnauthorizationException();
} else {
return new APITatarException(response.getResponse());
}
} catch (JSONException exception) {
return new APITatarException(response.getResponse());
}
}
}
}