All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mypurecloud.sdk.ApiClient Maven / Gradle / Ivy

The newest version!
package com.mypurecloud.sdk;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;

import com.mypurecloud.sdk.auth.Authentication;
import com.mypurecloud.sdk.auth.HttpBasicAuth;
import com.mypurecloud.sdk.auth.ApiKeyAuth;
import com.mypurecloud.sdk.auth.OAuth;
import com.mypurecloud.sdk.ApiDateFormat;


public class ApiClient {
    private Map defaultHeaderMap = Collections.synchronizedMap(new HashMap());
    private String basePath = "https://api.mypurecloud.com";
    private int connectionTimeout = 0;
    private boolean shouldThrowErrors = true;

    private CloseableHttpClient httpClient;
    private ObjectMapper objectMapper;

    private Map authentications;

    private int statusCode;
    private Header[]responseHeaders;

    private DateFormat dateFormat;
    private final SLF4JInterceptor loggingInterceptor;

    public ApiClient() {
        objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
        objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
        objectMapper.registerModule(new JodaModule());
        objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat());

        dateFormat = ApiClient.buildDefaultDateFormat();
        loggingInterceptor = new SLF4JInterceptor();

        // Set default User-Agent.
        setUserAgent("PureCloud SDK/0.58.4.140/java");

        // Setup authentications (key: authentication name, value: authentication).
        authentications = new HashMap();
        authentications.put("PureCloud Auth", new OAuth());
        // Prevent the authentications from being modified.
        authentications = Collections.unmodifiableMap(authentications);

        rebuildHttpClient();
    }

    public boolean getShouldThrowErrors() {
        return shouldThrowErrors;
    }

    public void setShouldThrowErrors(boolean shouldThrowErrors) {
        this.shouldThrowErrors = shouldThrowErrors;
    }

    public static DateFormat buildDefaultDateFormat() {
        DateFormat dateFormat = new ApiDateFormat();
        // Use UTC as the default time zone.
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        return dateFormat;
    }

    /**
     * Build the Client used to make HTTP requests with the latest settings,
     * i.e. objectMapper and debugging.
     */
    public ApiClient rebuildHttpClient() {
        RequestConfig.Builder requestBuilder = RequestConfig.custom()
                .setConnectTimeout(connectionTimeout)
                .setSocketTimeout(connectionTimeout)
                .setConnectionRequestTimeout(connectionTimeout);

        this.httpClient = HttpClients.custom()
                .setDefaultRequestConfig(requestBuilder.build())
                .addInterceptorFirst((HttpRequestInterceptor) loggingInterceptor)
                .addInterceptorFirst((HttpResponseInterceptor) loggingInterceptor)
                .build();
        return this;
    }

    /**
     * Returns the current object mapper used for JSON serialization/deserialization.
     * 

* Note: If you make changes to the object mapper, remember to set it back via * setObjectMapper in order to trigger HTTP client rebuilding. *

*/ public ObjectMapper getObjectMapper() { return objectMapper; } public ApiClient setObjectMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; // Need to rebuild the Client as it depends on object mapper. rebuildHttpClient(); return this; } public CloseableHttpClient getHttpClient() { return httpClient; } public ApiClient setHttpClient(CloseableHttpClient httpClient) { this.httpClient = httpClient; return this; } public String getBasePath() { return basePath; } public ApiClient setBasePath(String basePath) { this.basePath = basePath; return this; } /** * Gets the status code of the previous request */ public int getStatusCode() { return statusCode; } /** * Gets the response headers of the previous request */ public Header[] getResponseHeaders() { return responseHeaders; } /** * Get authentications (key: authentication name, value: authentication). */ 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. */ 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. */ 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 API key value for the first API key authentication. */ public void setApiKey(String apiKey) { for (Authentication auth : authentications.values()) { if (auth instanceof ApiKeyAuth) { ((ApiKeyAuth) auth).setApiKey(apiKey); return; } } throw new RuntimeException("No API key authentication configured!"); } /** * Helper method to set API key prefix for the first API key authentication. */ public void setApiKeyPrefix(String apiKeyPrefix) { for (Authentication auth : authentications.values()) { if (auth instanceof ApiKeyAuth) { ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); return; } } throw new RuntimeException("No API key authentication configured!"); } /** * Helper method to set access token for the first OAuth2 authentication. */ public void setAccessToken(String accessToken) { for (Authentication auth : authentications.values()) { if (auth instanceof OAuth) { ((OAuth) auth).setAccessToken(accessToken); return; } } throw new RuntimeException("No OAuth2 authentication configured!"); } /** * Set the User-Agent header's value (by adding to the default header map). */ 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 */ public ApiClient addDefaultHeader(String key, String value) { defaultHeaderMap.put(key, value); return this; } /** * @return the current logging detail level of this client */ public SLF4JInterceptor.DetailLevel getDetailLevel() { return loggingInterceptor.getDetailLevel(); } /** * Sets the logging detail level of this client * @param detailLevel - the level to set it to */ public void setDetailLevel(SLF4JInterceptor.DetailLevel detailLevel) { loggingInterceptor.setDetailLevel(detailLevel); } /** * Connect timeout (in milliseconds). */ public int getConnectTimeout() { return connectionTimeout; } /** * Set the connect timeout (in milliseconds). * A value of 0 means no timeout, otherwise values must be between 1 and * {@link Integer#MAX_VALUE}. */ public ApiClient setConnectTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; rebuildHttpClient(); return this; } /** * Get the date format used to parse/format date parameters. */ public DateFormat getDateFormat() { return dateFormat; } /** * Set the date format used to parse/format date parameters. */ public ApiClient setDateFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; // Also set the date format for model (de)serialization with Date properties. this.objectMapper.setDateFormat((DateFormat) dateFormat.clone()); // Need to rebuild the Client as objectMapper changes. rebuildHttpClient(); return this; } /** * Parse the given string into Date object. */ public Date parseDate(String str) { try { synchronized(dateFormat) { return dateFormat.parse(str); } } catch (java.text.ParseException e) { throw new RuntimeException(e); } } /** * Format the given Date object into string. */ public String formatDate(Date date) { synchronized(dateFormat) { return dateFormat.format(date); } } /** * Format the given parameter object into string. */ public String parameterToString(Object param) { if (param == null) { return ""; } else if (param instanceof Date) { return formatDate((Date) param); } else if (param instanceof Collection) { StringBuilder b = new StringBuilder(); for(Object o : (Collection)param) { if(b.length() > 0) { b.append(","); } b.append(String.valueOf(o)); } return b.toString(); } else { return String.valueOf(param); } } /* Format to {@code Pair} objects. */ public List parameterToPairs(String collectionFormat, String name, Object value){ List params = new ArrayList(); // preconditions if (name == null || name.isEmpty() || value == null) return params; Collection valueCollection = null; if (value instanceof Collection) { valueCollection = (Collection) value; } else { params.add(new Pair(name, parameterToString(value))); return params; } if (valueCollection.isEmpty()){ return params; } // get the collection format collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv // create the params based on the collection format if (collectionFormat.equals("multi")) { for (Object item : valueCollection) { params.add(new Pair(name, parameterToString(item))); } return params; } String delimiter = ","; if (collectionFormat.equals("csv")) { delimiter = ","; } else if (collectionFormat.equals("ssv")) { delimiter = " "; } else if (collectionFormat.equals("tsv")) { delimiter = "\t"; } else if (collectionFormat.equals("pipes")) { delimiter = "|"; } StringBuilder sb = new StringBuilder() ; for (Object item : valueCollection) { sb.append(delimiter); sb.append(parameterToString(item)); } params.add(new Pair(name, sb.substring(1))); return params; } /** * Check if the given MIME is a JSON MIME. * JSON MIME examples: * application/json * application/json; charset=UTF8 * APPLICATION/JSON */ public boolean isJsonMime(String mime) { return mime != null && mime.matches("(?i)application\\/json(;.*)?"); } /** * 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, * JSON will be used. */ public String selectHeaderContentType(String[] contentTypes) { if (contentTypes.length == 0) { 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. */ public String escapeString(String str) { try { return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); } catch (UnsupportedEncodingException e) { return str; } } /** * Serialize the given Java object into string according the given * Content-Type (only JSON is supported for now). */ public String serialize(Object obj) throws ApiException { try { return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException e) { throw new ApiException(e); } } /** * Build full URL by concatenating base path, the given sub path and query parameters. * * @param path The sub path * @param queryParams The query parameters * @return The full URL */ private String buildUrl(String path, List queryParams) { final StringBuilder url = new StringBuilder(); 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)); } } } return url.toString(); } private ApiResponse getAPIResponse(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames) throws ApiException, IOException { if (body != null && !formParams.isEmpty()) { throw new ApiException(500, "Cannot have body and form params"); } updateParamsForAuth(authNames, queryParams, headerParams); final String url = buildUrl(path, queryParams); // Build request object HttpUriRequest request; if ("GET".equals(method)) { HttpGet req = new HttpGet(url); request = req; } else if ("POST".equals(method)) { HttpPost req = new HttpPost(url); if (contentType.startsWith("application/x-www-form-urlencoded")) { List nvps = new ArrayList<>(); for (Entry param : formParams.entrySet()) { nvps.add(new BasicNameValuePair(param.getKey(), parameterToString(param.getValue()))); } req.setEntity(new UrlEncodedFormEntity(nvps)); } else { req.setEntity(new StringEntity(serialize(body), "UTF-8")); } request = req; } else if ("PUT".equals(method)) { HttpPut req = new HttpPut(url); req.setEntity(new StringEntity(serialize(body))); request = req; } else if ("DELETE".equals(method)) { HttpDelete req = new HttpDelete(url); request = req; } else if ("PATCH".equals(method)) { HttpPatch req = new HttpPatch(url); req.setEntity(new StringEntity(serialize(body))); request = req; } else { throw new ApiException(500, "unknown method type " + method); } // Add headers if (accept != null && !accept.isEmpty()) { request.setHeader("Accept", accept); } if (contentType != null && !contentType.isEmpty()) { request.setHeader("Content-Type", contentType); } for (String key : headerParams.keySet()) { request.setHeader(key, headerParams.get(key)); } for (String key : defaultHeaderMap.keySet()) { if (!headerParams.containsKey(key)) { request.setHeader(key, defaultHeaderMap.get(key)); } } CloseableHttpResponse response = null; ApiResponse apiResponse = new ApiResponse<>(request, null); response = httpClient.execute(request); apiResponse.setHttpResponse(response); return apiResponse; } /** * Invoke API by sending HTTP request with the given options. * * @param path The sub-path of the HTTP URL * @param method The request method, one of "GET", "POST", "PUT", and "DELETE" * @param queryParams The query parameters * @param body The request body object - if it is not binary, otherwise null * @param headerParams The header parameters * @param formParams The form parameters * @param accept The request's Accept header * @param contentType The request's Content-Type header * @param authNames The authentications to apply * @return The response body in type of string */ public ApiResponse invokeAPIVerbose(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames, TypeReference returnType) throws ApiException { ApiResponse response = null; try { try { response = getAPIResponse(path, method, queryParams, body, headerParams, formParams, accept, contentType, authNames); } catch (IOException e) { throw new ApiException(e); } int statusCode = response.getHttpResponse().getStatusLine().getStatusCode(); Header[] responseHeaders = response.getHttpResponse().getAllHeaders(); this.statusCode = statusCode; this.responseHeaders = responseHeaders; // Parse response body if (statusCode >= 200 && statusCode < 300) { HttpEntity entity = response.getHttpResponse().getEntity(); try { if (statusCode != 204 && entity != null && returnType != null) { String rawResponseBody = EntityUtils.toString(entity); response.setRawResponseBody(rawResponseBody); if (rawResponseBody != null && !rawResponseBody.isEmpty()) { response.setResponseObject((T) objectMapper.readValue(rawResponseBody, returnType)); } } } catch (IOException e) { throw new ApiException(e); } } else if (statusCode == HttpStatus.SC_NO_CONTENT) { // No body, it's cool. } else { String message = "error"; String respBody = null; HttpEntity entity = response.getHttpResponse().getEntity(); if (entity != null) { try { respBody = EntityUtils.toString(entity); message = respBody; } catch (RuntimeException | IOException e) { // e.printStackTrace(); } } throw new ApiException(statusCode, message, response.getResponseHeaders(), respBody); } return response; } catch (ApiException e) { if (shouldThrowErrors) throw e; else if (response == null) response = new ApiResponse<>(null, null); response.setException(e); return response; } finally { try { if (response != null && response.getHttpResponse() != null) { response.getHttpResponse().close(); } } catch (IOException e) { //e.printStackTrace(); } } } /** * Invoke API by sending HTTP request with the given options. * * @param path The sub-path of the HTTP URL * @param method The request method, one of "GET", "POST", "PUT", and "DELETE" * @param queryParams The query parameters * @param body The request body object - if it is not binary, otherwise null * @param headerParams The header parameters * @param formParams The form parameters * @param accept The request's Accept header * @param contentType The request's Content-Type header * @param authNames The authentications to apply * @return The response body in type of string */ public T invokeAPI(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames, TypeReference returnType) throws ApiException { T response = null; try { response = invokeAPIVerbose(path, method, queryParams, body, headerParams, formParams, accept, contentType, authNames, returnType).getResponseObject(); return response; } catch (ApiException e) { if (shouldThrowErrors) throw e; else return response; } } /** * Update query and header parameters based on authentication settings. * * @param authNames The authentications to apply */ private void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams) { for (String authName : authNames) { Authentication auth = authentications.get(authName); if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); auth.applyToParams(queryParams, headerParams); } } /** * Encode the given form parameters as request body. */ private String getXWWWFormUrlencodedParams(Map formParams) { StringBuilder formParamBuilder = new StringBuilder(); for (Entry param : formParams.entrySet()) { String valueStr = parameterToString(param.getValue()); try { formParamBuilder.append(URLEncoder.encode(param.getKey(), "utf8")) .append("=") .append(URLEncoder.encode(valueStr, "utf8")); formParamBuilder.append("&"); } catch (UnsupportedEncodingException e) { // move on to next } } String encodedFormParams = formParamBuilder.toString(); if (encodedFormParams.endsWith("&")) { encodedFormParams = encodedFormParams.substring(0, encodedFormParams.length() - 1); } return encodedFormParams; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy