package com.twilio.http; import com.google.common.base.Joiner; import com.google.common.collect.Range; import com.twilio.exception.ApiException; import com.twilio.exception.InvalidRequestException; import org.apache.commons.codec.binary.Base64; import org.joda.time.DateTime; import org.joda.time.LocalDate; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; public class Request { public static final String QUERY_STRING_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; public static final String QUERY_STRING_DATE_FORMAT = "yyyy-MM-dd"; private final HttpMethod method; private final String url; private final Map<String, List<String>> queryParams; private final Map<String, List<String>> postParams; private String username; private String password; /** * Create a new API request. * * @param method HTTP method * @param url url of request */ public Request(final HttpMethod method, final String url) { this.method = method; this.url = url; this.queryParams = new HashMap<>(); this.postParams = new HashMap<>(); } /** * Create a new API request. * * @param method HTTP method * @param domain Twilio domain * @param uri uri of request */ public Request(final HttpMethod method, final String domain, final String uri) { this(method, domain, uri, null); } /** * Create a new API request. * @param method HTTP Method * @param domain Twilio domain * @param uri uri of request * @param region region to make request */ public Request( final HttpMethod method, final String domain, final String uri, final String region ) { this.method = method; this.url = "https://" + Joiner.on(".").skipNulls().join(domain, region, "twilio", "com") + uri; this.queryParams = new HashMap<>(); this.postParams = new HashMap<>(); } public HttpMethod getMethod() { return method; } public String getUrl() { return url; } public void setAuth(final String username, final String password) { this.username = username; this.password = password; } /** * Create auth string from username and password. * * @return basic authentication string */ public String getAuthString() { String credentials = this.username + ":" + this.password; try { String encoded = new Base64().encodeAsString(credentials.getBytes("ascii")); return "Basic " + encoded; } catch (final UnsupportedEncodingException e) { throw new InvalidRequestException("It must be possible to encode credentials as ascii", credentials, e); } } public String getUsername() { return username; } public String getPassword() { return password; } public boolean requiresAuthentication() { return username != null || password != null; } /** * Build the URL for the request. * * @return URL for the request */ @SuppressWarnings("checkstyle:abbreviationaswordinname") public URL constructURL() { String params = encodeQueryParams(); String stringUri = url; if (params.length() > 0) { stringUri += "?" + params; } try { URI uri = new URI(stringUri); return uri.toURL(); } catch (final URISyntaxException e) { throw new ApiException("Bad URI: " + stringUri, e); } catch (final MalformedURLException e) { throw new ApiException("Bad URL: " + stringUri, e); } } /** * Add query parameters for date ranges. * * @param name name of query parameter * @param range date range */ public void addQueryDateRange(final String name, final Range<LocalDate> range) { if (range.hasLowerBound()) { String value = range.lowerEndpoint().toString(QUERY_STRING_DATE_FORMAT); addQueryParam(name + ">", value); } if (range.hasUpperBound()) { String value = range.upperEndpoint().toString(QUERY_STRING_DATE_FORMAT); addQueryParam(name + "<", value); } } /** * Add query parameters for date ranges. * * @param name name of query parameter * @param range date range */ public void addQueryDateTimeRange(final String name, final Range<DateTime> range) { if (range.hasLowerBound()) { String value = range.lowerEndpoint().toString(QUERY_STRING_DATE_TIME_FORMAT); addQueryParam(name + ">", value); } if (range.hasUpperBound()) { String value = range.upperEndpoint().toString(QUERY_STRING_DATE_TIME_FORMAT); addQueryParam(name + "<", value); } } /** * Add a query parameter. * * @param name name of parameter * @param value value of parameter */ public void addQueryParam(final String name, final String value) { addParam(queryParams, name, value); } /** * Add a form parameter. * * @param name name of parameter * @param value value of parameter */ public void addPostParam(final String name, final String value) { addParam(postParams, name, value); } private void addParam(final Map<String, List<String>> params, final String name, final String value) { if (!params.containsKey(name)) { params.put(name, new ArrayList<String>()); } params.get(name).add(value); } /** * Encode the form body. * * @return url encoded form body */ public String encodeFormBody() { return encodeParameters(postParams); } /** * Encode the query parameters. * * @return url encoded query parameters */ public String encodeQueryParams() { return encodeParameters(queryParams); } private static String encodeParameters(final Map<String, List<String>> params) { List<String> parameters = new ArrayList<>(); for (final Map.Entry<String, List<String>> entry : params.entrySet()) { try { String encodedName = URLEncoder.encode(entry.getKey(), "UTF-8"); for (final String value : entry.getValue()) { if (value == null) { continue; } String encodedValue = URLEncoder.encode(value, "UTF-8"); parameters.add(encodedName + "=" + encodedValue); } } catch (final UnsupportedEncodingException e) { throw new InvalidRequestException("Couldn't encode params", entry.getKey(), e); } } return Joiner.on("&").join(parameters); } public Map<String, List<String>> getQueryParams() { return queryParams; } public Map<String, List<String>> getPostParams() { return postParams; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Request other = (Request) o; return Objects.equals(this.method, other.method) && Objects.equals(this.url, other.url) && Objects.equals(this.username, other.username) && Objects.equals(this.password, other.password) && Objects.equals(this.queryParams, other.queryParams) && Objects.equals(this.postParams, other.postParams); } }