com.konfigthis.splitit.client.ApiClient Maven / Gradle / Ivy
/*
* splitit-web-api-v3
* Splitit's Web API
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by Konfig (https://konfigthis.com).
* Do not edit the class manually.
*/
package com.konfigthis.splitit.client;
import okhttp3.*;
import okhttp3.internal.http.HttpMethod;
import okhttp3.internal.tls.OkHostnameVerifier;
import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.logging.HttpLoggingInterceptor.Level;
import okio.Buffer;
import okio.BufferedSink;
import okio.Okio;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.konfigthis.splitit.client.auth.Authentication;
import com.konfigthis.splitit.client.auth.HttpBasicAuth;
import com.konfigthis.splitit.client.auth.HttpBearerAuth;
import com.konfigthis.splitit.client.auth.ApiKeyAuth;
import com.konfigthis.splitit.client.auth.OAuth;
import com.konfigthis.splitit.client.auth.RetryingOAuth;
import com.konfigthis.splitit.client.auth.OAuthFlow;
/**
* ApiClient class.
*/
public class ApiClient extends ApiClientCustom {
private String basePath = "https://web-api-v3.production.splitit.com";
private String tokenUrl = "https://id.production.splitit.com/connect/token";
private boolean debugging = false;
private Map defaultHeaderMap = new HashMap();
private Map defaultCookieMap = new HashMap();
private String tempFolderPath = null;
private Map authentications;
private DateFormat dateFormat;
private DateFormat datetimeFormat;
private boolean lenientDatetimeFormat;
private int dateLength;
private InputStream sslCaCert;
private boolean verifyingSsl;
private KeyManager[] keyManagers;
private OkHttpClient httpClient;
private JSON json;
private HttpLoggingInterceptor loggingInterceptor;
/**
* Basic constructor for ApiClient
*/
public ApiClient() {
this((OkHttpClient) null, null);
}
/**
* Basic constructor with custom OkHttpClient
*
* @param client a {@link okhttp3.OkHttpClient} object
*/
public ApiClient(OkHttpClient client) {
this(client, null);
}
public ApiClient(OkHttpClient client, Configuration configuration) {
init();
if (configuration != null) {
// Setting up authentications, if applicable, may rely on the following, so we initialize them first
setBasePath(configuration.host);
setTokenUrl(configuration.tokenUrl);
}
// Setup authentications (key: authentication name, value: authentication).
RetryingOAuth oauth = configuration != null ? setupOauth(configuration.clientId, configuration.clientSecret, null) : null;
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
if (client == null) {
initHttpClient(oauth != null ? Collections.singletonList(oauth) : Collections.emptyList());
} else {
this.httpClient = client;
}
if (configuration != null) {
setVerifyingSsl(configuration.verifyingSsl);
}
}
/**
* Constructor for ApiClient to support access token retry on 401/403 configured with client ID
*
* @param clientId client ID
*/
public ApiClient(String clientId) {
this(clientId, null, null);
}
/**
* Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters
*
* @param clientId client ID
* @param parameters a {@link java.util.Map} of parameters
*/
public ApiClient(String clientId, Map parameters) {
this(clientId, null, parameters);
}
/**
* Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters
*
* @param clientId client ID
* @param clientSecret client secret
* @param parameters a {@link java.util.Map} of parameters
*/
public ApiClient(String clientId, String clientSecret, Map parameters) {
this(null, clientId, clientSecret, parameters);
}
/**
* Constructor for ApiClient to support access token retry on 401/403 configured with base path, client ID, secret, and additional parameters
*
* @param basePath base path
* @param clientId client ID
* @param clientSecret client secret
* @param parameters a {@link java.util.Map} of parameters
*/
public ApiClient(String basePath, String clientId, String clientSecret, Map parameters) {
init();
if (basePath != null) {
this.basePath = basePath;
}
// Setup authentications (key: authentication name, value: authentication).
RetryingOAuth oauth = setupOauth(clientId, clientSecret, parameters);
initHttpClient(Collections.singletonList(oauth));
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
}
private RetryingOAuth setupOauth(String clientId, String clientSecret, Map parameters) {
if (!"".equals(getTokenUrl()) && !URI.create(getTokenUrl()).isAbsolute()) {
URI uri = URI.create(getBasePath());
setTokenUrl(uri.getScheme() + ":"
+ (uri.getAuthority() != null ? "//" + uri.getAuthority() : "")
+ getTokenUrl());
if (!URI.create(getTokenUrl()).isAbsolute()) {
throw new IllegalArgumentException("OAuth2 token URL must be an absolute URL");
}
}
RetryingOAuth oauth = new RetryingOAuth(getTokenUrl(), clientId, OAuthFlow.APPLICATION, clientSecret, parameters);
authentications.put("oauth", oauth);
return oauth;
}
private void initHttpClient() {
initHttpClient(Collections.emptyList());
}
private void initHttpClient(List interceptors) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addNetworkInterceptor(getProgressInterceptor());
for (Interceptor interceptor: interceptors) {
builder.addInterceptor(interceptor);
}
builder.readTimeout(0, TimeUnit.MILLISECONDS);
httpClient = builder.build();
}
private void init() {
verifyingSsl = true;
json = new JSON();
// Set default User-Agent.
setUserAgent("Konfig/3.0.3/java");
authentications = new HashMap();
}
/**
* Get base path
*
* @return Base path
*/
public String getBasePath() {
return basePath;
}
/**
* Set base path
*
* @param basePath Base path of the URL (e.g https://web-api-v3.production.splitit.com)
* @return An instance of OkHttpClient
*/
public ApiClient setBasePath(String basePath) {
// strip trailing slash from basePath
if (basePath != null && basePath.endsWith("/")) {
basePath = basePath.substring(0, basePath.length() - 1);
}
this.basePath = basePath;
return this;
}
/**
* Get token url
*
* @return token url
*/
public String getTokenUrl() {
return tokenUrl;
}
/**
* Set token url
*
* @param tokenUrl Token URL for oauth (e.g https://id.production.splitit.com/connect/token)
* @return An instance of OkHttpClient
*/
public ApiClient setTokenUrl(String tokenUrl) {
// strip trailing slash from tokenUrl
if (tokenUrl != null && tokenUrl.endsWith("/")) {
tokenUrl = tokenUrl.substring(0, tokenUrl.length() - 1);
}
this.tokenUrl = tokenUrl;
return this;
}
/**
* Get HTTP client
*
* @return An instance of OkHttpClient
*/
public OkHttpClient getHttpClient() {
return httpClient;
}
/**
* Set HTTP client, which must never be null.
*
* @param newHttpClient An instance of OkHttpClient
* @return Api Client
* @throws java.lang.NullPointerException when newHttpClient is null
*/
public ApiClient setHttpClient(OkHttpClient newHttpClient) {
this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!");
return this;
}
/**
* Get JSON
*
* @return JSON object
*/
public JSON getJSON() {
return json;
}
/**
* Set JSON
*
* @param json JSON object
* @return Api client
*/
public ApiClient setJSON(JSON json) {
this.json = json;
return this;
}
/**
* True if isVerifyingSsl flag is on
*
* @return True if isVerifySsl flag is on
*/
public boolean isVerifyingSsl() {
return verifyingSsl;
}
/**
* Configure whether to verify certificate and hostname when making https requests.
* Default to true.
* NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks.
*
* @param verifyingSsl True to verify TLS/SSL connection
* @return ApiClient
*/
public ApiClient setVerifyingSsl(boolean verifyingSsl) {
this.verifyingSsl = verifyingSsl;
applySslSettings();
return this;
}
/**
* Get SSL CA cert.
*
* @return Input stream to the SSL CA cert
*/
public InputStream getSslCaCert() {
return sslCaCert;
}
/**
* Configure the CA certificate to be trusted when making https requests.
* Use null to reset to default.
*
* @param sslCaCert input stream for SSL CA cert
* @return ApiClient
*/
public ApiClient setSslCaCert(InputStream sslCaCert) {
this.sslCaCert = sslCaCert;
applySslSettings();
return this;
}
/**
* Getter for the field keyManagers
.
*
* @return an array of {@link javax.net.ssl.KeyManager} objects
*/
public KeyManager[] getKeyManagers() {
return keyManagers;
}
/**
* Configure client keys to use for authorization in an SSL session.
* Use null to reset to default.
*
* @param managers The KeyManagers to use
* @return ApiClient
*/
public ApiClient setKeyManagers(KeyManager[] managers) {
this.keyManagers = managers;
applySslSettings();
return this;
}
/**
* Getter for the field dateFormat
.
*
* @return a {@link java.text.DateFormat} object
*/
public DateFormat getDateFormat() {
return dateFormat;
}
/**
* Setter for the field dateFormat
.
*
* @param dateFormat a {@link java.text.DateFormat} object
* @return a {@link com.konfigthis.splitit.client.ApiClient} object
*/
public ApiClient setDateFormat(DateFormat dateFormat) {
JSON.setDateFormat(dateFormat);
return this;
}
/**
* Set SqlDateFormat.
*
* @param dateFormat a {@link java.text.DateFormat} object
* @return a {@link com.konfigthis.splitit.client.ApiClient} object
*/
public ApiClient setSqlDateFormat(DateFormat dateFormat) {
JSON.setSqlDateFormat(dateFormat);
return this;
}
/**
* Set OffsetDateTimeFormat.
*
* @param dateFormat a {@link java.time.format.DateTimeFormatter} object
* @return a {@link com.konfigthis.splitit.client.ApiClient} object
*/
public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
JSON.setOffsetDateTimeFormat(dateFormat);
return this;
}
/**
* Set LocalDateFormat.
*
* @param dateFormat a {@link java.time.format.DateTimeFormatter} object
* @return a {@link com.konfigthis.splitit.client.ApiClient} object
*/
public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) {
JSON.setLocalDateFormat(dateFormat);
return this;
}
/**
* Set LenientOnJson.
*
* @param lenientOnJson a boolean
* @return a {@link com.konfigthis.splitit.client.ApiClient} object
*/
public ApiClient setLenientOnJson(boolean lenientOnJson) {
JSON.setLenientOnJson(lenientOnJson);
return this;
}
/**
* Get authentications (key: authentication name, value: authentication).
*
* @return Map of authentication objects
*/
public Map getAuthentications() {
return authentications;
}
/**
* Get authentication for the given name.
*
* @param authName The authentication name
* @return The authentication, null if not found
*/
public Authentication getAuthentication(String authName) {
return authentications.get(authName);
}
/**
* Helper method to set username for the first HTTP basic authentication.
*
* @param username Username
*/
public void setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set password for the first HTTP basic authentication.
*
* @param password Password
*/
public void setPassword(String password) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setPassword(password);
return;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set access token for the first OAuth2 authentication.
*
* @param accessToken Access token
*/
public void setAccessToken(String accessToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setAccessToken(accessToken);
return;
}
}
}
/**
* Set the User-Agent header's value (by adding to the default header map).
*
* @param userAgent HTTP request's user agent
* @return ApiClient
*/
public ApiClient setUserAgent(String userAgent) {
addDefaultHeader("User-Agent", userAgent);
return this;
}
/**
* Add a default header.
*
* @param key The header's key
* @param value The header's value
* @return ApiClient
*/
public ApiClient addDefaultHeader(String key, String value) {
defaultHeaderMap.put(key, value);
return this;
}
/**
* Add a default cookie.
*
* @param key The cookie's key
* @param value The cookie's value
* @return ApiClient
*/
public ApiClient addDefaultCookie(String key, String value) {
defaultCookieMap.put(key, value);
return this;
}
/**
* Check that whether debugging is enabled for this API client.
*
* @return True if debugging is enabled, false otherwise.
*/
public boolean isDebugging() {
return debugging;
}
/**
* Enable/disable debugging for this API client.
*
* @param debugging To enable (true) or disable (false) debugging
* @return ApiClient
*/
public ApiClient setDebugging(boolean debugging) {
if (debugging != this.debugging) {
if (debugging) {
loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(Level.BODY);
httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build();
} else {
final OkHttpClient.Builder builder = httpClient.newBuilder();
builder.interceptors().remove(loggingInterceptor);
httpClient = builder.build();
loggingInterceptor = null;
}
}
this.debugging = debugging;
return this;
}
/**
* The path of temporary folder used to store downloaded files from endpoints
* with file response. The default value is null
, i.e. using
* the system's default temporary folder.
*
* @see createTempFile
* @return Temporary folder path
*/
public String getTempFolderPath() {
return tempFolderPath;
}
/**
* Set the temporary folder path (for downloading files)
*
* @param tempFolderPath Temporary folder path
* @return ApiClient
*/
public ApiClient setTempFolderPath(String tempFolderPath) {
this.tempFolderPath = tempFolderPath;
return this;
}
/**
* Get connection timeout (in milliseconds).
*
* @return Timeout in milliseconds
*/
public int getConnectTimeout() {
return httpClient.connectTimeoutMillis();
}
/**
* Sets the connect timeout (in milliseconds).
* A value of 0 means no timeout, otherwise values must be between 1 and
* {@link java.lang.Integer#MAX_VALUE}.
*
* @param connectionTimeout connection timeout in milliseconds
* @return Api client
*/
public ApiClient setConnectTimeout(int connectionTimeout) {
httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build();
return this;
}
/**
* Get read timeout (in milliseconds).
*
* @return Timeout in milliseconds
*/
public int getReadTimeout() {
return httpClient.readTimeoutMillis();
}
/**
* Sets the read timeout (in milliseconds).
* A value of 0 means no timeout, otherwise values must be between 1 and
* {@link java.lang.Integer#MAX_VALUE}.
*
* @param readTimeout read timeout in milliseconds
* @return Api client
*/
public ApiClient setReadTimeout(int readTimeout) {
httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build();
return this;
}
/**
* Get write timeout (in milliseconds).
*
* @return Timeout in milliseconds
*/
public int getWriteTimeout() {
return httpClient.writeTimeoutMillis();
}
/**
* Sets the write timeout (in milliseconds).
* A value of 0 means no timeout, otherwise values must be between 1 and
* {@link java.lang.Integer#MAX_VALUE}.
*
* @param writeTimeout connection timeout in milliseconds
* @return Api client
*/
public ApiClient setWriteTimeout(int writeTimeout) {
httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build();
return this;
}
/**
* Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one)
*
* @return Token request builder
*/
public TokenRequestBuilder getTokenEndPoint() {
for (Authentication apiAuth : authentications.values()) {
if (apiAuth instanceof RetryingOAuth) {
RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth;
return retryingOAuth.getTokenRequestBuilder();
}
}
return null;
}
/**
* Format the given parameter object into string.
*
* @param param Parameter
* @return String representation of the parameter
*/
public String parameterToString(Object param) {
if (param == null) {
return "";
} else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) {
//Serialize to json string and remove the " enclosing characters
String jsonStr = JSON.serialize(param);
return jsonStr.substring(1, jsonStr.length() - 1);
} else if (param instanceof Collection) {
StringBuilder b = new StringBuilder();
for (Object o : (Collection) param) {
if (b.length() > 0) {
b.append(",");
}
b.append(o);
}
return b.toString();
} else {
return String.valueOf(param);
}
}
/**
* Formats the specified query parameter to a list containing a single {@code Pair} object.
*
* Note that {@code value} must not be a collection.
*
* @param name The name of the parameter.
* @param value The value of the parameter.
* @return A list containing a single {@code Pair} object.
*/
public List parameterToPair(String name, Object value) {
List params = new ArrayList();
// preconditions
if (name == null || name.isEmpty() || value == null || value instanceof Collection) {
return params;
}
params.add(new Pair(name, parameterToString(value)));
return params;
}
/**
* Formats the specified collection query parameters to a list of {@code Pair} objects.
*
* Note that the values of each of the returned Pair objects are percent-encoded.
*
* @param collectionFormat The collection format of the parameter.
* @param name The name of the parameter.
* @param value The value of the parameter.
* @return A list of {@code Pair} objects.
*/
public List parameterToPairs(String collectionFormat, String name, Collection value) {
List params = new ArrayList();
// preconditions
if (name == null || name.isEmpty() || value == null || value.isEmpty()) {
return params;
}
// create the params based on the collection format
if ("multi".equals(collectionFormat)) {
for (Object item : value) {
params.add(new Pair(name, escapeString(parameterToString(item))));
}
return params;
}
// collectionFormat is assumed to be "csv" by default
String delimiter = ",";
// escape all delimiters except commas, which are URI reserved
// characters
if ("ssv".equals(collectionFormat)) {
delimiter = escapeString(" ");
} else if ("tsv".equals(collectionFormat)) {
delimiter = escapeString("\t");
} else if ("pipes".equals(collectionFormat)) {
delimiter = escapeString("|");
}
StringBuilder sb = new StringBuilder();
for (Object item : value) {
sb.append(delimiter);
sb.append(escapeString(parameterToString(item)));
}
params.add(new Pair(name, sb.substring(delimiter.length())));
return params;
}
/**
* Formats the specified collection path parameter to a string value.
*
* @param collectionFormat The collection format of the parameter.
* @param value The value of the parameter.
* @return String representation of the parameter
*/
public String collectionPathParameterToString(String collectionFormat, Collection value) {
// create the value based on the collection format
if ("multi".equals(collectionFormat)) {
// not valid for path params
return parameterToString(value);
}
// collectionFormat is assumed to be "csv" by default
String delimiter = ",";
if ("ssv".equals(collectionFormat)) {
delimiter = " ";
} else if ("tsv".equals(collectionFormat)) {
delimiter = "\t";
} else if ("pipes".equals(collectionFormat)) {
delimiter = "|";
}
StringBuilder sb = new StringBuilder() ;
for (Object item : value) {
sb.append(delimiter);
sb.append(parameterToString(item));
}
return sb.substring(delimiter.length());
}
/**
* Sanitize filename by removing path.
* e.g. ../../sun.gif becomes sun.gif
*
* @param filename The filename to be sanitized
* @return The sanitized filename
*/
public String sanitizeFilename(String filename) {
return filename.replaceAll(".*[/\\\\]", "");
}
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
* application/vnd.company+json
* "* / *" is also default to JSON
* @param mime MIME (Multipurpose Internet Mail Extensions)
* @return True if the given MIME is JSON, false otherwise.
*/
public boolean isJsonMime(String mime) {
String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
return mime != null && (mime.matches(jsonMime) || mime.equals("*/*"));
}
/**
* Select the Accept header's value from the given accepts array:
* if JSON exists in the given array, use it;
* otherwise use all of them (joining into a string)
*
* @param accepts The accepts array to select from
* @return The Accept header to use. If the given array is empty,
* null will be returned (not to set the Accept header explicitly).
*/
public String selectHeaderAccept(String[] accepts) {
if (accepts.length == 0) {
return null;
}
for (String accept : accepts) {
if (isJsonMime(accept)) {
return accept;
}
}
return StringUtil.join(accepts, ",");
}
/**
* Select the Content-Type header's value from the given array:
* if JSON exists in the given array, use it;
* otherwise use the first one of the array.
*
* @param contentTypes The Content-Type array to select from
* @return The Content-Type header to use. If the given array is empty,
* returns null. If it matches "any", JSON will be used.
*/
public String selectHeaderContentType(String[] contentTypes) {
if (contentTypes.length == 0) {
return null;
}
if (contentTypes[0].equals("*/*")) {
return "application/json";
}
for (String contentType : contentTypes) {
if (isJsonMime(contentType)) {
return contentType;
}
}
return contentTypes[0];
}
/**
* Escape the given string to be used as URL query value.
*
* @param str String to be escaped
* @return Escaped string
*/
public String escapeString(String str) {
try {
return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
return str;
}
}
/**
* Deserialize response body to Java object, according to the return type and
* the Content-Type response header.
*
* @param Type
* @param response HTTP response
* @param returnType The type of the Java object
* @return The deserialized Java object
* @throws com.konfigthis.splitit.client.ApiException If fail to deserialize response body, i.e. cannot read response body
* or the Content-Type of the response is not supported.
*/
@SuppressWarnings("unchecked")
public T deserialize(Response response, Type returnType) throws ApiException {
if (response == null || returnType == null) {
return null;
}
if ("byte[]".equals(returnType.toString())) {
// Handle binary response (byte array).
try {
return (T) response.body().bytes();
} catch (IOException e) {
throw new ApiException(e);
}
} else if (returnType.equals(File.class)) {
// Handle file downloading.
return (T) downloadFileFromResponse(response);
}
String respBody;
try {
if (response.body() != null)
respBody = response.body().string();
else
respBody = null;
} catch (IOException e) {
throw new ApiException(e);
}
if (respBody == null || "".equals(respBody)) {
return null;
}
String contentType = response.headers().get("Content-Type");
if (contentType == null) {
// ensuring a default content type
contentType = "application/json";
}
if (isJsonMime(contentType)) {
return JSON.deserialize(respBody, returnType);
} else if (returnType.equals(String.class)) {
// Expecting string, return the raw response body.
return (T) respBody;
} else {
throw new ApiException(
"Content type \"" + contentType + "\" is not supported for type: " + returnType,
response.code(),
response.headers().toMultimap(),
respBody, response.receivedResponseAtMillis() - response.sentRequestAtMillis());
}
}
/**
* Serialize the given Java object into request body according to the object's
* class and the request Content-Type.
*
* @param obj The Java object
* @param contentType The request Content-Type
* @return The serialized request body
* @throws com.konfigthis.splitit.client.ApiException If fail to serialize the given object
*/
public RequestBody serialize(Object obj, String contentType) throws ApiException {
if (obj instanceof byte[]) {
// Binary (byte array) body parameter support.
return RequestBody.create((byte[]) obj, MediaType.parse(contentType));
} else if (obj instanceof File) {
// File body parameter support.
return RequestBody.create((File) obj, MediaType.parse(contentType));
} else if ("text/plain".equals(contentType) && obj instanceof String) {
return RequestBody.create((String) obj, MediaType.parse(contentType));
} else if (isJsonMime(contentType)) {
String content;
if (obj != null) {
content = JSON.serialize(obj);
} else {
content = null;
}
return RequestBody.create(content, MediaType.parse(contentType));
} else if (obj instanceof String) {
return RequestBody.create((String) obj, MediaType.parse(contentType));
} else {
throw new ApiException("Content type \"" + contentType + "\" is not supported");
}
}
/**
* Download file from the given response.
*
* @param response An instance of the Response object
* @throws com.konfigthis.splitit.client.ApiException If fail to read file content from response and write to disk
* @return Downloaded file
*/
public File downloadFileFromResponse(Response response) throws ApiException {
try {
File file = prepareDownloadFile(response);
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeAll(response.body().source());
sink.close();
return file;
} catch (IOException e) {
throw new ApiException(e);
}
}
/**
* Prepare file for download
*
* @param response An instance of the Response object
* @return Prepared file for the download
* @throws java.io.IOException If fail to prepare file for download
*/
public File prepareDownloadFile(Response response) throws IOException {
String filename = null;
String contentDisposition = response.header("Content-Disposition");
if (contentDisposition != null && !"".equals(contentDisposition)) {
// Get filename from the Content-Disposition header.
Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
Matcher matcher = pattern.matcher(contentDisposition);
if (matcher.find()) {
filename = sanitizeFilename(matcher.group(1));
}
}
String prefix = null;
String suffix = null;
if (filename == null) {
prefix = "download-";
suffix = "";
} else {
int pos = filename.lastIndexOf(".");
if (pos == -1) {
prefix = filename + "-";
} else {
prefix = filename.substring(0, pos) + "-";
suffix = filename.substring(pos);
}
// Files.createTempFile requires the prefix to be at least three characters long
if (prefix.length() < 3)
prefix = "download-";
}
if (tempFolderPath == null)
return Files.createTempFile(prefix, suffix).toFile();
else
return Files.createTempFile(Paths.get(tempFolderPath), prefix, suffix).toFile();
}
/**
* {@link #execute(Call, Type)}
*
* @param Type
* @param call An instance of the Call object
* @return ApiResponse<T>
* @throws com.konfigthis.splitit.client.ApiException If fail to execute the call
*/
public ApiResponse execute(Call call) throws ApiException {
return execute(call, null);
}
/**
* Execute HTTP call and deserialize the HTTP response body into the given return type.
*
* @param returnType The return type used to deserialize HTTP response body
* @param The return type corresponding to (same with) returnType
* @param call Call
* @return ApiResponse object containing response status, headers and
* data, which is a Java object deserialized from response body and would be null
* when returnType is null.
* @throws com.konfigthis.splitit.client.ApiException If fail to execute the call
*/
public ApiResponse execute(Call call, Type returnType) throws ApiException {
try {
Response response = call.execute();
T data = handleResponse(response, returnType);
return new ApiResponse(
call.request(),
response.code(),
response.headers().toMultimap(),
data,
response.receivedResponseAtMillis() -
response.sentRequestAtMillis());
} catch (IOException e) {
throw new ApiException(e);
}
}
/**
* {@link #executeAsync(Call, Type, ApiCallback)}
*
* @param Type
* @param call An instance of the Call object
* @param callback ApiCallback<T>
*/
public void executeAsync(Call call, ApiCallback callback) {
executeAsync(call, null, callback);
}
/**
* Execute HTTP call asynchronously.
*
* @param Type
* @param call The callback to be executed when the API call finishes
* @param returnType Return type
* @param callback ApiCallback
* @see #execute(Call, Type)
*/
@SuppressWarnings("unchecked")
public void executeAsync(Call call, final Type returnType, final ApiCallback callback) {
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.onFailure(new ApiException(e), 0, null);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
T result;
try {
result = (T) handleResponse(response, returnType);
} catch (ApiException e) {
callback.onFailure(e, response.code(), response.headers().toMultimap());
return;
} catch (Exception e) {
callback.onFailure(new ApiException(e), response.code(), response.headers().toMultimap());
return;
}
callback.onSuccess(result, response.code(), response.headers().toMultimap());
}
});
}
/**
* Handle the given response, return the deserialized object when the response is successful.
*
* @param Type
* @param response Response
* @param returnType Return type
* @return Type
* @throws com.konfigthis.splitit.client.ApiException If the response has an unsuccessful status code or
* fail to deserialize the response body
*/
public T handleResponse(Response response, Type returnType) throws ApiException {
if (response.isSuccessful()) {
if (returnType == null || response.code() == 204) {
// returning null if the returnType is not defined,
// or the status code is 204 (No Content)
if (response.body() != null) {
try {
response.body().close();
} catch (Exception e) {
throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap(), response.receivedResponseAtMillis() - response.sentRequestAtMillis());
}
}
return null;
} else {
return deserialize(response, returnType);
}
} else {
String respBody = null;
if (response.body() != null) {
try {
respBody = response.body().string();
} catch (IOException e) {
throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap(), response.receivedResponseAtMillis() - response.sentRequestAtMillis());
}
}
throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody, response.receivedResponseAtMillis() - response.sentRequestAtMillis());
}
}
/**
* Build HTTP call with the given options.
*
* @param baseUrl The base URL
* @param path The sub-path of the HTTP URL
* @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE"
* @param queryParams The query parameters
* @param collectionQueryParams The collection query parameters
* @param body The request body object
* @param headerParams The header parameters
* @param cookieParams The cookie parameters
* @param formParams The form parameters
* @param authNames The authentications to apply
* @param callback Callback for upload/download progress
* @return The HTTP call
* @throws com.konfigthis.splitit.client.ApiException If fail to serialize the request body object
*/
public Call buildCall(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException {
Request request = buildRequest(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback);
return httpClient.newCall(request);
}
/**
* Build an HTTP request with the given options.
*
* @param baseUrl The base URL
* @param path The sub-path of the HTTP URL
* @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE"
* @param queryParams The query parameters
* @param collectionQueryParams The collection query parameters
* @param body The request body object
* @param headerParams The header parameters
* @param cookieParams The cookie parameters
* @param formParams The form parameters
* @param authNames The authentications to apply
* @param callback Callback for upload/download progress
* @return The HTTP request
* @throws com.konfigthis.splitit.client.ApiException If fail to serialize the request body object
*/
public Request buildRequest(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException {
requestBeforeHook(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, this);
// prepare HTTP request body
RequestBody reqBody;
String contentType = headerParams.get("Content-Type");
if (!HttpMethod.permitsRequestBody(method)) {
reqBody = null;
} else if ("application/x-www-form-urlencoded".equals(contentType)) {
reqBody = buildRequestBodyFormEncoding(formParams);
} else if ("multipart/form-data".equals(contentType)) {
reqBody = buildRequestBodyMultipart(formParams);
} else if (body == null) {
if ("DELETE".equals(method)) {
// allow calling DELETE without sending a request body
reqBody = null;
} else {
// use an empty request body (for POST, PUT and PATCH)
reqBody = RequestBody.create("", contentType == null ? null : MediaType.parse(contentType));
}
} else {
reqBody = serialize(body, contentType);
}
String payload = requestBodyToString(reqBody);
// update parameters with authentication settings
updateParamsForAuth(authNames, queryParams, headerParams, cookieParams, payload, method);
final String url = buildUrl(baseUrl, path, queryParams, collectionQueryParams);
requestAfterHook(url, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, payload, this);
final Request.Builder reqBuilder = new Request.Builder().url(url);
processHeaderParams(headerParams, reqBuilder);
processCookieParams(cookieParams, reqBuilder);
// Associate callback with request (if not null) so interceptor can
// access it when creating ProgressResponseBody
reqBuilder.tag(callback);
Request request = null;
if (callback != null && reqBody != null) {
ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback);
request = reqBuilder.method(method, progressRequestBody).build();
} else {
request = reqBuilder.method(method, reqBody).build();
}
return request;
}
/**
* Build full URL by concatenating base path, the given sub path and query parameters.
*
* @param baseUrl The base URL
* @param path The sub path
* @param queryParams The query parameters
* @param collectionQueryParams The collection query parameters
* @return The full URL
*/
public String buildUrl(String baseUrl, String path, List queryParams, List collectionQueryParams) {
final StringBuilder url = new StringBuilder();
if (baseUrl != null) {
url.append(baseUrl).append(path);
} else {
url.append(basePath).append(path);
}
if (queryParams != null && !queryParams.isEmpty()) {
// support (constant) query string in `path`, e.g. "/posts?draft=1"
String prefix = path.contains("?") ? "&" : "?";
for (Pair param : queryParams) {
if (param.getValue() != null) {
if (prefix != null) {
url.append(prefix);
prefix = null;
} else {
url.append("&");
}
String value = parameterToString(param.getValue());
url.append(escapeString(param.getName())).append("=").append(escapeString(value));
}
}
}
if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) {
String prefix = url.toString().contains("?") ? "&" : "?";
for (Pair param : collectionQueryParams) {
if (param.getValue() != null) {
if (prefix != null) {
url.append(prefix);
prefix = null;
} else {
url.append("&");
}
String value = parameterToString(param.getValue());
// collection query parameter value already escaped as part of parameterToPairs
url.append(escapeString(param.getName())).append("=").append(value);
}
}
}
return url.toString();
}
/**
* Set header parameters to the request builder, including default headers.
*
* @param headerParams Header parameters in the form of Map
* @param reqBuilder Request.Builder
*/
public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) {
for (Entry param : headerParams.entrySet()) {
reqBuilder.header(param.getKey(), parameterToString(param.getValue()));
}
for (Entry header : defaultHeaderMap.entrySet()) {
if (!headerParams.containsKey(header.getKey())) {
reqBuilder.header(header.getKey(), parameterToString(header.getValue()));
}
}
}
/**
* Set cookie parameters to the request builder, including default cookies.
*
* @param cookieParams Cookie parameters in the form of Map
* @param reqBuilder Request.Builder
*/
public void processCookieParams(Map cookieParams, Request.Builder reqBuilder) {
for (Entry param : cookieParams.entrySet()) {
reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue()));
}
for (Entry param : defaultCookieMap.entrySet()) {
if (!cookieParams.containsKey(param.getKey())) {
reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue()));
}
}
}
/**
* Update query and header parameters based on authentication settings.
*
* @param authNames The authentications to apply
* @param queryParams List of query parameters
* @param headerParams Map of header parameters
* @param cookieParams Map of cookie parameters
* @param payload HTTP request body
* @param method HTTP method
* @throws com.konfigthis.splitit.client.ApiException If fails to update the parameters
*/
public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams,
Map cookieParams, String payload, String method) throws ApiException {
for (String authName : authNames) {
Authentication auth = authentications.get(authName);
if (auth == null) {
throw new RuntimeException("Authentication undefined: " + authName);
}
auth.applyToParams(queryParams, headerParams, cookieParams, payload, method);
}
}
/**
* Build a form-encoding request body with the given form parameters.
*
* @param formParams Form parameters in the form of Map
* @return RequestBody
*/
public RequestBody buildRequestBodyFormEncoding(Map formParams) {
okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder();
for (Entry param : formParams.entrySet()) {
formBuilder.add(param.getKey(), parameterToString(param.getValue()));
}
return formBuilder.build();
}
/**
* Build a multipart (file uploading) request body with the given form parameters,
* which could contain text fields and file fields.
*
* @param formParams Form parameters in the form of Map
* @return RequestBody
*/
public RequestBody buildRequestBodyMultipart(Map formParams) {
MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
for (Entry param : formParams.entrySet()) {
if (param.getValue() instanceof File) {
File file = (File) param.getValue();
addPartToMultiPartBuilder(mpBuilder, param.getKey(), file);
} else if (param.getValue() instanceof List) {
List list = (List) param.getValue();
for (Object item: list) {
if (item instanceof File) {
addPartToMultiPartBuilder(mpBuilder, param.getKey(), (File) item);
} else {
addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue());
}
}
} else {
addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue());
}
}
return mpBuilder.build();
}
/**
* Guess Content-Type header from the given file (defaults to "application/octet-stream").
*
* @param file The given file
* @return The guessed Content-Type
*/
public String guessContentTypeFromFile(File file) {
String contentType = URLConnection.guessContentTypeFromName(file.getName());
if (contentType == null) {
return "application/octet-stream";
} else {
return contentType;
}
}
/**
* Add a Content-Disposition Header for the given key and file to the MultipartBody Builder.
*
* @param mpBuilder MultipartBody.Builder
* @param key The key of the Header element
* @param file The file to add to the Header
*/
private void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, File file) {
Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\"; filename=\"" + file.getName() + "\"");
MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file));
mpBuilder.addPart(partHeaders, RequestBody.create(file, mediaType));
}
/**
* Add a Content-Disposition Header for the given key and complex object to the MultipartBody Builder.
*
* @param mpBuilder MultipartBody.Builder
* @param key The key of the Header element
* @param obj The complex object to add to the Header
*/
private void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, Object obj) {
RequestBody requestBody;
if (obj instanceof String) {
requestBody = RequestBody.create((String) obj, MediaType.parse("text/plain"));
} else {
String content;
if (obj != null) {
content = JSON.serialize(obj);
} else {
content = null;
}
requestBody = RequestBody.create(content, MediaType.parse("application/json"));
}
Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\"");
mpBuilder.addPart(partHeaders, requestBody);
}
/**
* Get network interceptor to add it to the httpClient to track download progress for
* async requests.
*/
private Interceptor getProgressInterceptor() {
return new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
final Request request = chain.request();
final Response originalResponse = chain.proceed(request);
if (request.tag() instanceof ApiCallback) {
final ApiCallback callback = (ApiCallback) request.tag();
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), callback))
.build();
}
return originalResponse;
}
};
}
/**
* Apply SSL related settings to httpClient according to the current values of
* verifyingSsl and sslCaCert.
*/
private void applySslSettings() {
try {
TrustManager[] trustManagers;
HostnameVerifier hostnameVerifier;
if (!verifyingSsl) {
trustManagers = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
} else {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
if (sslCaCert == null) {
trustManagerFactory.init((KeyStore) null);
} else {
char[] password = null; // Any password will work.
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
KeyStore caKeyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = "ca" + (index++);
caKeyStore.setCertificateEntry(certificateAlias, certificate);
}
trustManagerFactory.init(caKeyStore);
}
trustManagers = trustManagerFactory.getTrustManagers();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, new SecureRandom());
httpClient = httpClient.newBuilder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
.hostnameVerifier(hostnameVerifier)
.build();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Convert the HTTP request body to a string.
*
* @param requestBody The HTTP request object
* @return The string representation of the HTTP request body
* @throws com.konfigthis.splitit.client.ApiException If fail to serialize the request body object into a string
*/
private String requestBodyToString(RequestBody requestBody) throws ApiException {
if (requestBody != null) {
try {
final Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
throw new ApiException(e);
}
}
// empty http request body
return "";
}
}