diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..dff5f3a5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java diff --git a/README.md b/README.md index ea2fdf06..78b32ed1 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ -# HelloSign Java SDK +# HelloSign Java SDK +[![Build Status](https://travis-ci.org/HelloFax/hellosign-java-sdk.svg?branch=master)](https://travis-ci.org/HelloFax/hellosign-java-sdk) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hellosign/hellosign-java-sdk/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hellosign/hellosign-java-sdk/) [![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.hellosign/hellosign-java-sdk/badge.svg)](http://www.javadoc.io/doc/com.hellosign/hellosign-java-sdk/) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.hellosign/hellosign-java-sdk/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.hellosign/hellosign-java-sdk/) -[![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.hellosign/hellosign-java-sdk/badge.svg)](http://www.javadoc.io/doc/com.hellosign/hellosign-java-sdk/) - -Use the `hellosign-java-sdk` to get your Java app connected to HelloSign's API in minutes. +Get your Java app connected to HelloSign's API in jiffy. ## Installing -The SDK is built and deployed to the [Central Maven repository](https://repo1.maven.org/maven2/com/hellosign/hellosign-java-sdk/). Add it to your Maven project by including the following `` in your `pom.xml`: +SDK releases are published to Maven's [Central repository](https://repo1.maven.org/maven2/com/hellosign/hellosign-java-sdk/): ```xml @@ -18,24 +16,22 @@ The SDK is built and deployed to the [Central Maven repository](https://repo1.ma ``` -> NOTE: It is compiled with and targeted for Java 7 and depends on the [SL4J 1.7.5](http://www.slf4j.org/) and JSON v20090211 libraries. If your project already includes these, use the JAR without dependencies by removing the `` element in the example above. - Alternatively, you can build the JAR yourself: - mvn clean package -DskipTests + mvn clean package -Locate the JAR file in the `target` directory and place it on your project classpath. +Place `target/hellosign-java-sdk-.jar` on your project classpath. ## Usage -All HelloSign API requests are made using the `HelloSignClient`. This class must be initialized with your [API key](https://www.hellosign.com/home/myAccount/current_tab/integrations#api). +First initialize an instance of the `HelloSignClient` with your [API key](https://www.hellosign.com/home/myAccount/current_tab/integrations#api): + ```java HelloSignClient client = new HelloSignClient(apiKey); ``` -The following examples assume the client has been initialized this way. ### Create a Signature Request -Construct a `SignatureRequest` object and populate it with request details. When you provide this object to the `HelloSignClient.sendSignatureRequest()` method, an HTTP request will be made and the method will return a `SignatureRequest` object. Use this object to read details about the new signature request. + ```java SignatureRequest request = new SignatureRequest(); request.setSubject("NDA"); @@ -45,11 +41,9 @@ request.addFile(new File("nda.pdf")); SignatureRequest response = client.sendSignatureRequest(request); System.out.println(response.toString()); -// Prints the JSON response to the console ``` ### Retrieve Templates -The HelloSign API provides paged lists of templates and signature requests (`client.getSignatureRequests()`). These lists are represented as objects that can be iterated upon: ```java TemplateList templateList = client.getTemplates(); @@ -58,7 +52,7 @@ for (Template template : templateList) { } ``` -The paged list can also be filtered by a particular parameter and value: +Or filter the paged list: ```java TemplateList templateList = client.getTemplates(); @@ -69,17 +63,16 @@ for (Template template : filteredList) { ``` ### Create a Signature Request from a Template -Using a `template` object retrieved from the API, create a signature request with it: + ```java TemplateSignatureRequest request = new TemplateSignatureRequest(); -request.setTemplateId(template.getId()); +request.setTemplateId(templateId); request.setSigner("Client", "george@example.com", "George"); request.setCC("Accounting", "accounting@hellosign.com"); request.addCustomFieldValue("Cost", "$20,000"); SignatureRequest response = client.sendTemplateSignatureRequest(request); System.out.println(response.toString()); -// Prints the JSON response to the console ``` ### Checking the Status of a Signature Request @@ -97,11 +90,8 @@ if (response.isComplete()) { ## Reference -The complete JavaDoc is kindly hosted at [javadoc.io](http://www.javadoc.io/): -http://www.javadoc.io/doc/com.hellosign/hellosign-java-sdk - - +* [API Reference](http://www.javadoc.io/doc/com.hellosign/hellosign-java-sdk) +* [Sample JSP web application](https://www.github.com/cmpaul/jellosign) ## License diff --git a/pom.xml b/pom.xml index f79acc08..30b4f755 100644 --- a/pom.xml +++ b/pom.xml @@ -1,240 +1,221 @@ - - 4.0.0 - com.hellosign - hellosign-java-sdk - jar - 3.5.6 - HelloSign Java SDK - https://github.com/HelloFax/hellosign-java-sdk - - Chris Paul (chris@hellosign.com) - 7.6.14.v20131031 - 1.7.5 - UTF-8 - - - - org.json - json - 20090211 - - - org.slf4j - slf4j-api - ${slf4jVersion} - - - junit - junit - 4.11 - test - - - commons-fileupload - commons-fileupload - 1.3 - test - - - javax.servlet - javax.servlet-api - 3.0.1 - test - - - org.slf4j - slf4j-simple - ${slf4jVersion} - test - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - src/main/resources - true - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - maven-compiler-plugin - 3.0 - - 1.7 - 1.7 - - - - test-compile - process-test-sources - - testCompile - - - 1.7 - 1.7 - - - - - - maven-assembly-plugin - - - package - - single - - - - - - jar-with-dependencies - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - org.mortbay.jetty - jetty-maven-plugin - ${jettyVersion} - - 10 - true - ${basedir}/src/test/webapp - ${basedir}/target/classes - ${basedir}/target/test-classes - src/test/webapp/WEB-INF/web.properties - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 - - - - java - - - - - com.hellosign.sdk.callback.ExampleCallbackServer - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - true - - - - sign-artifacts - verify - - sign - - - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - ${basedir}/src/test/webapp/javadoc - - - - - Use the HelloSign Java SDK to connect your Java app to HelloSign's service in microseconds! - - HelloSign - https://www.hellosign.com/ - - + 4.0.0 + com.hellosign + hellosign-java-sdk + jar + 4.0.0 + HelloSign Java SDK https://github.com/HelloFax/hellosign-java-sdk - scm:git:git@github.com:HelloFax/hellosign-java-sdk.git - scm:git:git://github.com/HelloFax/hellosign-java-sdk.git - - - - MIT License - http://www.opensource.org/licenses/mit-license.php - repo - - - - - chris-hellosign - Chris Paul - chris@hellosign.com - https://www.hellosign.com - HelloSign - https://www.hellosign.com - - architect - developer - - America/Los_Angeles - - https://www.gravatar.com/avatar/7970e197406612872f6895a71a803f08 - - - + + Chris Paul (chris@hellosign.com) + 1.7.22 + UTF-8 + true + true + + + + org.json + json + 20090211 + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + junit + junit + 4.11 + test + + + org.slf4j + slf4j-simple + ${slf4jVersion} + test + + + org.mockito + mockito-core + 2.3.4 + test + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + release-sign-artifacts + + + performRelease + true + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + true + + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + + + + + maven-compiler-plugin + 3.6.1 + + 1.7 + 1.7 + + + + test-compile + process-test-sources + + testCompile + + + 1.7 + 1.7 + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-assembly-plugin + + + package + + single + + + + + + jar-with-dependencies + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + ${basedir}/src/test/webapp/javadoc + + + + + Use the HelloSign Java SDK to connect your Java app to HelloSign's service in microseconds! + + HelloSign + https://www.hellosign.com/ + + + https://github.com/HelloFax/hellosign-java-sdk + scm:git:git@github.com:HelloFax/hellosign-java-sdk.git + scm:git:git://github.com/HelloFax/hellosign-java-sdk.git + + + + MIT License + http://www.opensource.org/licenses/mit-license.php + repo + + + + + chris-hellosign + Chris Paul + chris@hellosign.com + https://www.hellosign.com + HelloSign + https://www.hellosign.com + + architect + developer + + America/Los_Angeles + + https://www.gravatar.com/avatar/7970e197406612872f6895a71a803f08 + + + diff --git a/src/main/java/com/hellosign/sdk/HelloSignClient.java b/src/main/java/com/hellosign/sdk/HelloSignClient.java index 01187af0..1244d115 100644 --- a/src/main/java/com/hellosign/sdk/HelloSignClient.java +++ b/src/main/java/com/hellosign/sdk/HelloSignClient.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -25,30 +25,14 @@ */ import java.io.File; -import java.io.Serializable; import java.lang.reflect.Constructor; import java.net.HttpURLConnection; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.Map; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import org.json.JSONException; import org.json.JSONObject; -import com.hellosign.sdk.http.AbstractHttpRequest; import com.hellosign.sdk.http.Authentication; -import com.hellosign.sdk.http.HttpDeleteRequest; -import com.hellosign.sdk.http.HttpGetRequest; -import com.hellosign.sdk.http.HttpPostRequest; -import com.hellosign.sdk.http.HttpPutRequest; +import com.hellosign.sdk.http.HttpClient; import com.hellosign.sdk.resource.AbstractRequest; import com.hellosign.sdk.resource.AbstractResourceList; import com.hellosign.sdk.resource.Account; @@ -68,12 +52,12 @@ import com.hellosign.sdk.resource.support.TemplateList; /** - * You'll need the HelloSignClient to do just about everything, from creating + * You'll need the HelloSignClient to do just about everything, from creating * signatures to updating account information. * * To use this class, instantiate a client with valid credentials like so: * - * HelloSignClient client = new HelloSignClient(user, key); + * HelloSignClient client = new HelloSignClient(user, key); * * Then, use the client to perform your requests. The client uses the Unirest * library to perform its HTTP requests. (http://unirest.io/java.html). @@ -83,364 +67,250 @@ public class HelloSignClient { public static final String DEFAULT_ENCODING = "UTF-8"; - - // ====================================================================== - // HelloSign API URL Endpoints - // ====================================================================== - - private static final String API_VERSION = "v3"; - - private String URL_HELLOSIGN; - - // The base URL can be overridden by setting the "hellosign.base.url" - // system property. - private static final String URL_API_BASE = "https://api.hellosign.com"; - - private String URL_OAUTH_TOKEN; - - // The base URL can be overridden by setting the "hellosign.oauth.base.url" - // system property. - private static final String URL_OAUTH_TOKEN_PRODUCTION = "https://www.hellosign.com/oauth/token"; - - private String URL_API; - private String URL_ACCOUNT; - private String URL_VALIDATE_ACCOUNT; - private String URL_ACCOUNT_CREATE; - private String URL_TEAM; - private String URL_TEAM_CREATE; - private String URL_TEAM_DESTROY; - private String URL_TEAM_ADD_MEMBER; - private String URL_TEAM_REMOVE_MEMBER; - private String URL_SIGNATURE_REQUEST; - private String URL_SIGNATURE_REQUEST_LIST; - private String URL_SIGNATURE_REQUEST_SEND; - private String URL_TEMPLATE; - private String URL_TEMPLATE_FILE; - private String URL_TEMPLATE_LIST; - private String URL_TEMPLATE_ADD_USER; - private String URL_TEMPLATE_REMOVE_USER; - private String URL_TEMPLATE_DELETE; - private String URL_TEMPLATE_CREATE_EMBEDDED_DRAFT; // Embedded Templates - private String URL_TEMPLATE_SIGNATURE_REQUEST; - private String URL_SIGNATURE_REQUEST_CANCEL; - private String URL_SIGNATURE_REQUEST_REMIND; - private String URL_SIGNATURE_REQUEST_FINAL_COPY; - private String URL_SIGNATURE_REQUEST_FILES; - private String URL_SIGNATURE_REQUEST_UPDATE; - private String URL_SIGNATURE_REQUEST_EMBEDDED; - private String URL_SIGNATURE_REQUEST_EMBEDDED_TEMPLATE; - private String URL_EMBEDDED_SIGN_URL; - private String URL_EMBEDDED_EDIT_URL; // Embedded Templates - private String URL_UNCLAIMED_DRAFT_CREATE; - private String URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED; - private String URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE; - private String URL_API_APP; - private String URL_API_APP_LIST; - - private String URL_PARAM_FILE_TYPE = "file_type"; - + public static final String DEFAULT_BASE_API_URL = "https://api.hellosign.com/v3"; + public static final String DEFAULT_OAUTH_TOKEN_URL = "https://app.hellosign.com/oauth/token"; + + public static final String ACCOUNT_URI = "/account"; + public static final String VALIDATE_ACCOUNT_URI = "/account/validate"; + public static final String ACCOUNT_CREATE_URI = "/account/create"; + public static final String TEAM_URI = "/team"; + public static final String TEAM_CREATE_URI = "/team/create"; + public static final String TEAM_DESTROY_URI = "/team/destroy"; + public static final String TEAM_ADD_MEMBER_URI = "/team/add_member"; + public static final String TEAM_REMOVE_MEMBER_URI = "/team/remove_member"; + public static final String SIGNATURE_REQUEST_URI = "/signature_request"; + public static final String SIGNATURE_REQUEST_LIST_URI = "/signature_request/list"; + public static final String SIGNATURE_REQUEST_SEND_URI = "/signature_request/send"; + public static final String TEMPLATE_URI = "/template"; + public static final String TEMPLATE_FILE_URI = "/template/files"; + public static final String TEMPLATE_LIST_URI = "/template/list"; + public static final String TEMPLATE_ADD_USER_URI = "/template/add_user"; + public static final String TEMPLATE_REMOVE_USER_URI = "/template/remove_user"; + public static final String TEMPLATE_DELETE_URI = "/template/delete"; + public static final String TEMPLATE_CREATE_EMBEDDED_DRAFT_URI = "/template/create_embedded_draft"; + public static final String TEMPLATE_SIGNATURE_REQUEST_URI = "/signature_request/send_with_template"; + public static final String SIGNATURE_REQUEST_CANCEL_URI = "/signature_request/cancel"; + public static final String SIGNATURE_REQUEST_REMIND_URI = "/signature_request/remind"; + public static final String SIGNATURE_REQUEST_FINAL_COPY_URI = "/signature_request/final_copy"; + public static final String SIGNATURE_REQUEST_FILES_URI = "/signature_request/files"; + public static final String SIGNATURE_REQUEST_UPDATE_URI = "/signature_request/update"; + public static final String SIGNATURE_REQUEST_EMBEDDED_URI = "/signature_request/create_embedded"; + public static final String SIGNATURE_REQUEST_EMBEDDED_TEMPLATE_URI = "/signature_request/create_embedded_with_template"; + public static final String EMBEDDED_SIGN_URL_URI = "/embedded/sign_url"; + public static final String EMBEDDED_EDIT_URL_URI = "/embedded/edit_url"; + public static final String UNCLAIMED_DRAFT_CREATE_URI = "/unclaimed_draft/create"; + public static final String UNCLAIMED_DRAFT_CREATE_EMBEDDED_URI = "/unclaimed_draft/create_embedded"; + public static final String UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE_URI = "/unclaimed_draft/create_embedded_with_template"; + public static final String API_APP_URI = "/api_app"; + public static final String API_APP_LIST_URI = "/api_app/list"; + + public static final String PARAM_FILE_TYPE_URI = "file_type"; + public static final String PARAM_TEMPLATE_GET_URL = "get_url"; public static final String FINAL_COPY_FILE_NAME = "final-copy"; public static final String FINAL_COPY_FILE_EXT = "pdf"; - public static final String FILES_FILE_NAME = "files"; public static final String FILES_FILE_EXT = "pdf"; - public static final String TEMPLATE_FILE_NAME = "template"; public static final String TEMPLATE_FILE_EXT = "pdf"; - public static final String OAUTH_CODE = "code"; public static final String OAUTH_STATE = "state"; public static final String OAUTH_GRANT_TYPE = "grant_type"; public static final String OAUTH_REFRESH_TOKEN = "refresh_token"; public static final String OAUTH_GRANT_TYPE_AUTHORIZE_CODE = "authorization_code"; public static final String OAUTH_GRANT_TYPE_REFRESH_TOKEN = "refresh_token"; - public static final String CLIENT_SECRET = "client_secret"; public static final String CLIENT_ID = "client_id"; + public static final String EMBEDDED_TEMPLATE_SKIP_SIGNER_ROLES = "skip_signer_roles"; + public static final String EMBEDDED_TEMPLATE_SKIP_SUBJECT_MESSAGE = "skip_subject_message"; - private Authentication auth = new Authentication(); + private Authentication auth; + private HttpClient httpClient; - private HelloSignClient() { - URL_HELLOSIGN = URL_API_BASE; + // The base URL for all standard API endpoints, which can be + // overridden by setting the "hellosign.base.url" system property. + private String BASE_URI; + + // The base URL for retrieving an OAuth tokens, which can be + // overridden by setting the "hellosign.oauth.base.url" system property. + private String OAUTH_TOKEN_URL; + + /** + * Default constructor for injection of dependencies (testing). + * @param client HttpClient + * @param auth Authentication + * @see #HelloSignClient(String) + */ + protected HelloSignClient(HttpClient client, Authentication auth) { + this.httpClient = client; + this.auth = auth; + + // Set overrides if present + BASE_URI = DEFAULT_BASE_API_URL; String baseUrl = System.getProperty("hellosign.base.url"); if (baseUrl != null && !baseUrl.isEmpty()) { - URL_HELLOSIGN = baseUrl; + BASE_URI = baseUrl; } - URL_OAUTH_TOKEN = URL_OAUTH_TOKEN_PRODUCTION; + OAUTH_TOKEN_URL = DEFAULT_OAUTH_TOKEN_URL; String customOauthToken = System.getProperty("hellosign.oauth.base.url"); if (customOauthToken != null && !customOauthToken.isEmpty()) { - URL_OAUTH_TOKEN = customOauthToken; - } - String disableSslCheck = System.getProperty("hellosign.disable.ssl"); - if (disableSslCheck != null && "true".equalsIgnoreCase(disableSslCheck)) { - disableStrictSSL(); + OAUTH_TOKEN_URL = customOauthToken; } - initApiEndpoints(); - } - - private void initApiEndpoints() { - URL_API = URL_HELLOSIGN + "/" + API_VERSION; - URL_ACCOUNT = URL_API + "/account"; - URL_VALIDATE_ACCOUNT = URL_ACCOUNT + "/verify"; - URL_ACCOUNT_CREATE = URL_ACCOUNT + "/create"; - URL_TEAM = URL_API + "/team"; - URL_TEAM_CREATE = URL_TEAM + "/create"; - URL_TEAM_DESTROY = URL_TEAM + "/destroy"; - URL_TEAM_ADD_MEMBER = URL_TEAM + "/add_member"; - URL_TEAM_REMOVE_MEMBER = URL_TEAM + "/remove_member"; - URL_SIGNATURE_REQUEST = URL_API + "/signature_request"; - URL_SIGNATURE_REQUEST_LIST = URL_SIGNATURE_REQUEST + "/list"; - URL_SIGNATURE_REQUEST_SEND = URL_SIGNATURE_REQUEST + "/send"; - URL_TEMPLATE = URL_API + "/template"; - URL_TEMPLATE_FILE = URL_TEMPLATE + "/files"; - URL_TEMPLATE_LIST = URL_TEMPLATE + "/list"; - URL_TEMPLATE_ADD_USER = URL_TEMPLATE + "/add_user"; - URL_TEMPLATE_REMOVE_USER = URL_TEMPLATE + "/remove_user"; - URL_TEMPLATE_DELETE = URL_TEMPLATE + "/delete"; - URL_TEMPLATE_CREATE_EMBEDDED_DRAFT = URL_TEMPLATE + "/create_embedded_draft"; - URL_TEMPLATE_SIGNATURE_REQUEST = URL_SIGNATURE_REQUEST + "/send_with_template"; - URL_SIGNATURE_REQUEST_CANCEL = URL_SIGNATURE_REQUEST + "/cancel"; - URL_SIGNATURE_REQUEST_REMIND = URL_SIGNATURE_REQUEST + "/remind"; - URL_SIGNATURE_REQUEST_FINAL_COPY = URL_SIGNATURE_REQUEST + "/final_copy"; - URL_SIGNATURE_REQUEST_FILES = URL_SIGNATURE_REQUEST + "/files"; - URL_SIGNATURE_REQUEST_UPDATE = URL_SIGNATURE_REQUEST + "/update"; - URL_SIGNATURE_REQUEST_EMBEDDED = URL_SIGNATURE_REQUEST + "/create_embedded"; - URL_SIGNATURE_REQUEST_EMBEDDED_TEMPLATE = URL_SIGNATURE_REQUEST + "/create_embedded_with_template"; - URL_EMBEDDED_SIGN_URL = URL_API + "/embedded/sign_url"; - URL_EMBEDDED_EDIT_URL = URL_API + "/embedded/edit_url"; - URL_UNCLAIMED_DRAFT_CREATE = URL_API + "/unclaimed_draft/create"; - URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED = URL_API + "/unclaimed_draft/create_embedded"; - URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE = URL_API + "/unclaimed_draft/create_embedded_with_template"; - URL_API_APP = URL_API + "/api_app"; - URL_API_APP_LIST = URL_API_APP + "/list"; } /** * Creates a new HelloSign client using your API key. * - * Your HelloSign API key can be found on the Settings page: - * https://www.hellosign.com/home/myAccount/current_tab/integrations - * * @param apiKey String API key - */ - public HelloSignClient(String apiKey) { - this(); + * @throws HelloSignException thrown if there's a problem setting the + * credentials + * @see Account + * Settings + */ + public HelloSignClient(String apiKey) throws HelloSignException { + this(new HttpClient(), new Authentication(apiKey)); auth.setApiKey(apiKey); } /** - * Creates a new HelloSign client using your website account's - * email address and password. - * - * Note: This method is not suggested. You're using the API, so - * sign up for an API key already! + * Creates a new HelloSign client using then given Authentication object. * - * https://www.hellosign.com/home/myAccount/current_tab/integrations - * - * @param email String email - * @param password String password - * @throws HelloSignException thrown if there is a problem setting - * the credentials + * @param auth Authentication used primarily for setting OAuth token/secret + * @throws HelloSignException thrown if there's a problem setting the credentials */ - public HelloSignClient(String email, String password) - throws HelloSignException { - this(); - auth.setWebsiteCredentials(email, password); + public HelloSignClient(Authentication auth) throws HelloSignException { + this(new HttpClient(), auth); } /** - * Create a client with the provided authentication object. - * @param auth HelloSignAuthentication - * @throws HelloSignException thrown if the HelloSignAuthentication - * parameters are invalid or null + * Allows overriding of the authentication mechanism. Used + * mainly for setting an OAuth token/secret. + * @param auth */ - public HelloSignClient(Authentication auth) - throws HelloSignException { - this(); - this.auth = new Authentication(auth); + public void setAuthentication(Authentication auth) { + this.auth = auth; + } + + /** + * Retrieves the authentication details being used by this client. Used for + * testing purposes. + * + * @return Authentication + */ + protected Authentication getAuth() { + return auth; } /** * Returns the current user's account information. + * * @return Account - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Account getAccount() throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_ACCOUNT, auth); - JSONObject json = request.getJsonResponse(); - return new Account(json); + return new Account(httpClient.withAuth(auth).get(BASE_URI + ACCOUNT_URI).asJson()); } /** - * Returns true if an account exists with the provided email address. Note this is - * limited to the visibility of the currently authenticated user. + * Returns true if an account exists with the provided email address. Note + * this is limited to the visibility of the currently authenticated user. + * * @param email String email address * @return true if the account exists, false otherwise - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException Thrown if there's a problem communicating with + * the HelloSign API. */ public boolean isAccountValid(String email) throws HelloSignException { - if (email == null) { + if (email == null || email.isEmpty()) { return false; } - Map params = - new HashMap(); - params.put("email_address", email); - HttpPostRequest request = new HttpPostRequest( - URL_VALIDATE_ACCOUNT, params, auth); - JSONObject response = request.getJsonResponse(); - if (response.has(Account.ACCOUNT_KEY)) { - try { - JSONObject account = response.getJSONObject(Account.ACCOUNT_KEY); - if (account.has(Account.ACCOUNT_EMAIL_ADDRESS)) { - return email.equalsIgnoreCase( - account.getString(Account.ACCOUNT_EMAIL_ADDRESS)); - } - } catch (JSONException ex) { - throw new HelloSignException(ex); - } - } - return false; + Account account = new Account(httpClient.withAuth(auth).withPostField(Account.ACCOUNT_EMAIL_ADDRESS, email) + .post(BASE_URI + VALIDATE_ACCOUNT_URI).asJson()); + return (account.hasEmail() && email.equalsIgnoreCase(account.getEmail())); } /** - * Updates the current user's callback URL. + * Update your account callback URL. + * + * This URL is used to notify you of any signature request events that occur + * when your account is a party -- e.g., sender or signer/recipient. + * * @param callback String URL * @return Account - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Account setCallback(String callback) throws HelloSignException { - Map properties = new HashMap(); - properties.put(Account.ACCOUNT_CALLBACK_URL, callback); - HttpPostRequest request = new HttpPostRequest(URL_ACCOUNT, properties, auth); - JSONObject json = request.getJsonResponse(); - return new Account(json); + return new Account(httpClient.withAuth(auth).withPostField(Account.ACCOUNT_CALLBACK_URL, callback) + .post(BASE_URI + ACCOUNT_URI).asJson()); } /** - * Creates a new HelloSign account. The user will still need to validate their email address - * to complete the creation process to set a password. Note: This request does not require - * authentication, so just performs the basic POST. + * Creates a new HelloSign account. The user will still need to validate + * their email address to complete the creation process to set a password. + * Note: This request does not require authentication, so just performs the + * basic POST. + * * @param email String New user's email address * @return Account New user's account information - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Account createAccount(String email) throws HelloSignException { return createAccount(email, null, null); } /** - * Creates a new HelloSign account. The user will still need to validate their email address - * to complete the creation process. - * - * Note: This request does not require authentication, so just performs the basic POST. + * Creates a new HelloSign account and provides OAuth app credentials to + * automatically generate an OAuth token with the user Account response. * * @param email String New user's email address - * @param password String New user's password - * @return Account New user's account information - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. - * @deprecated as of 3.1.1, replaced by {@link #createAccount(String)} - */ - public Account createAccount(String email, String password) throws HelloSignException { - return createAccount(email, password, null, null); - } - - /** - * Creates a new HelloSign account and provides OAuth app credentials to automatically - * generate an OAuth token with the user Account response. - * @param email String New user's email address * @param clientId String Client ID * @param clientSecret String App secret * @return Account New user's account information - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Account createAccount(String email, String clientId, String clientSecret) throws HelloSignException { - Map fields = new HashMap(); - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, email); + HttpClient client = httpClient.withAuth(auth).withPostField(Account.ACCOUNT_EMAIL_ADDRESS, email); if (clientId != null && clientSecret != null) { - fields.put(CLIENT_ID, clientId); - fields.put(CLIENT_SECRET, clientSecret); + client = client.withPostField(CLIENT_ID, clientId).withPostField(CLIENT_SECRET, clientSecret); } - HttpPostRequest request = new HttpPostRequest(URL_ACCOUNT_CREATE, - fields, auth); - JSONObject json = request.getJsonResponse(); - return new Account(json); - } - - /** - * Creates a new HelloSign account and provides OAuth app credentials to automatically - * generate an OAuth token with the user Account response. - * @param email String New user's email address - * @param password String New user's password (NOTE: WILL BE IGNORED BY THE API) - * @param clientId String Client ID - * @param clientSecret String App secret - * @return Account New user's account information - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. - * @deprecated as of 3.1.1, replaced by {@link #createAccount(String, String, String)} - */ - public Account createAccount(String email, String password, String clientId, String clientSecret) throws HelloSignException { - Map fields = new HashMap(); - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, email); - - // Deprecated - we no longer allow setting a password when creating an account - fields.put(Account.ACCOUNT_PASSWORD, password); - - if (clientId != null && clientSecret != null) { - fields.put(CLIENT_ID, clientId); - fields.put(CLIENT_SECRET, clientSecret); + JSONObject response = client.post(BASE_URI + ACCOUNT_CREATE_URI).asJson(); + JSONObject copy; + try { + copy = new JSONObject(response.toString()); + } catch (JSONException e) { + throw new HelloSignException(e); } - HttpPostRequest request = new HttpPostRequest(URL_ACCOUNT_CREATE, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Account(json); - } - - /** - * Performs an OAuth request and returns the necessary data for authorizing an API - * application. - * @param code String OAuth code - * @param clientId String OAuth client ID - * @param secret String OAuth secret - * @return OauthData object containing OAuth token details - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. - */ - public OauthData getOauthData(String code, String clientId, String secret) - throws HelloSignException { - return getOauthData(code, clientId, secret, true); + OauthData data = new OauthData(copy); + Account account = new Account(response); + account.setOauthData(data); + return account; } /** - * Performs an OAuth request and returns the necessary data for authorizing an API - * application, and will automatically set the access token and code for making - * authenticated requests with this client. + * Performs an OAuth request and returns the necessary data for authorizing + * an API application, and will automatically set the access token and code + * for making authenticated requests with this client. + * * @param code String OAuth code * @param clientId String OAuth client ID * @param secret String OAuth secret - * @param autoSetRequestToken true if the token should be immediately applied to - * this client + * @param state String OAuth client state + * @param autoSetRequestToken true if the token should be applied to this + * client for future requests * @return OauthData object containing OAuth token details - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. - */ - public OauthData getOauthData( - String code, String clientId, String secret, boolean autoSetRequestToken) - throws HelloSignException { - Map fields = new HashMap(); - // TODO: What should this be? - fields.put(OAUTH_STATE, "demo"); - fields.put(OAUTH_CODE, code); - fields.put(CLIENT_ID, clientId); - fields.put(OAUTH_GRANT_TYPE, OAUTH_GRANT_TYPE_AUTHORIZE_CODE); - fields.put(CLIENT_SECRET, secret); - HttpPostRequest request = new HttpPostRequest(URL_OAUTH_TOKEN, fields, auth); - JSONObject json = request.getJsonResponse(); - OauthData data = new OauthData(json); + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. + */ + public OauthData getOauthData(String code, String clientId, String secret, String state, + boolean autoSetRequestToken) throws HelloSignException { + OauthData data = new OauthData(httpClient.withAuth(auth).withPostField(OAUTH_STATE, state) + .withPostField(OAUTH_CODE, code).withPostField(CLIENT_ID, clientId) + .withPostField(OAUTH_GRANT_TYPE, OAUTH_GRANT_TYPE_AUTHORIZE_CODE).withPostField(CLIENT_SECRET, secret) + .post(OAUTH_TOKEN_URL).asJson()); if (data != null && autoSetRequestToken) { setAccessToken(data.getAccessToken(), data.getTokenType()); } @@ -448,21 +318,17 @@ public OauthData getOauthData( } /** - * Refreshes the OauthData for this client with the provided refresh - * token. + * Refreshes the OauthData for this client with the provided refresh token. + * * @param refreshToken String * @return OauthData new OAuthData returned from HelloSign - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public OauthData refreshOauthData(String refreshToken) - throws HelloSignException { - Map fields = new HashMap(); - fields.put(OAUTH_GRANT_TYPE, OAUTH_GRANT_TYPE_REFRESH_TOKEN); - fields.put(OAUTH_REFRESH_TOKEN, refreshToken); - HttpPostRequest request = new HttpPostRequest(URL_OAUTH_TOKEN, fields, auth); - JSONObject json = request.getJsonResponse(); - OauthData data = new OauthData(json); + public OauthData refreshOauthData(String refreshToken) throws HelloSignException { + OauthData data = new OauthData( + httpClient.withAuth(auth).withPostField(OAUTH_GRANT_TYPE, OAUTH_GRANT_TYPE_REFRESH_TOKEN) + .withPostField(OAUTH_REFRESH_TOKEN, refreshToken).post(OAUTH_TOKEN_URL).asJson()); if (data != null) { setAccessToken(data.getAccessToken(), data.getTokenType()); } @@ -471,153 +337,137 @@ public OauthData refreshOauthData(String refreshToken) /** * Retrieves the Team for the current user account. + * * @return Team - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Team getTeam() throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_TEAM, auth); - return new Team(request.getJsonResponse()); + return new Team(httpClient.withAuth(auth).get(BASE_URI + TEAM_URI).asJson()); } /** * Creates a new team for the current user with the given name. + * * @param teamName String team name * @return Team - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Team createTeam(String teamName) throws HelloSignException { - Map fields = new HashMap(); - fields.put(Team.TEAM_NAME, teamName); - HttpPostRequest request = new HttpPostRequest(URL_TEAM_CREATE, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Team(json); + return new Team( + httpClient.withAuth(auth).withPostField(Team.TEAM_NAME, teamName).post(BASE_URI + TEAM_URI).asJson()); } /** * Destroys the current user's team. - * @return int HTTP Status - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * + * @return boolean if destroy was successful + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public int destroyTeam() throws HelloSignException { - HttpPostRequest request = new HttpPostRequest(URL_TEAM_DESTROY, auth); - return request.getHttpResponseCode(); + public boolean destroyTeam() throws HelloSignException { + return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth).post(BASE_URI + TEAM_DESTROY_URI).asHttpCode(); } /** * Updates the current user's team name. + * * @param teamName String team name - * @return Team - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @return Team + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Team updateTeamName(String teamName) throws HelloSignException { - Map fields = new HashMap(); - fields.put(Team.TEAM_NAME, teamName); - HttpPostRequest request = new HttpPostRequest(URL_TEAM, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Team(json); + return new Team( + httpClient.withAuth(auth).withPostField(Team.TEAM_NAME, teamName).post(BASE_URI + TEAM_URI).asJson()); } /** * Adds the user to the current user's team. + * * @param idOrEmail String new team member's account ID or email address * @return Team - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Team inviteTeamMember(String idOrEmail) throws HelloSignException { - Map fields = new HashMap(); - if (idOrEmail.contains("@")) { - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, idOrEmail); - } else { - fields.put(Account.ACCOUNT_ID, idOrEmail); - } - HttpPostRequest request = new HttpPostRequest(URL_TEAM_ADD_MEMBER, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Team(json); + String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS + : Account.ACCOUNT_ID; + return new Team( + httpClient.withAuth(auth).withPostField(key, idOrEmail).post(BASE_URI + TEAM_ADD_MEMBER_URI).asJson()); } /** - * Removes the team member indicated by the user account ID or email address. + * Removes the team member indicated by the user account ID or email + * address. + * * @param idOrEmail String removed team member's account ID or email address * @return Team - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Team removeTeamMember(String idOrEmail) throws HelloSignException { - Map fields = new HashMap(); - if (idOrEmail.contains("@")) { - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, idOrEmail); - } else { - fields.put(Account.ACCOUNT_ID, idOrEmail); - } - HttpPostRequest request = new HttpPostRequest(URL_TEAM_REMOVE_MEMBER, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Team(json); + String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS + : Account.ACCOUNT_ID; + return new Team(httpClient.withAuth(auth).withPostField(key, idOrEmail).post(BASE_URI + TEAM_REMOVE_MEMBER_URI) + .asJson()); } /** * Retrieves a Signature Request with the given ID. + * * @param id String signature ID * @return SignatureRequest - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public SignatureRequest getSignatureRequest(String id) throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_SIGNATURE_REQUEST + "/" + id, auth); - return new SignatureRequest(request.getJsonResponse()); + String url = BASE_URI + SIGNATURE_REQUEST_URI + "/" + id; + return new SignatureRequest(httpClient.withAuth(auth).get(url).asJson()); } /** - * Retrieves the current user's signature requests. The resulting object represents - * a paged query result. The page information can be retrieved on from the ListInfo - * object on the SignatureRequestList. + * Retrieves the current user's signature requests. The resulting object + * represents a paged query result. The page information can be retrieved on + * from the ListInfo object on the SignatureRequestList. + * * @return SignatureRequestList - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public SignatureRequestList getSignatureRequests() - throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_SIGNATURE_REQUEST_LIST, auth); - return new SignatureRequestList(request.getJsonResponse()); + public SignatureRequestList getSignatureRequests() throws HelloSignException { + return new SignatureRequestList(httpClient.withAuth(auth).get(BASE_URI + SIGNATURE_REQUEST_LIST_URI).asJson()); } /** * Retrieves a specific page of the current user's signature requests. + * * @param page int * @return SignatureRequestList - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public SignatureRequestList getSignatureRequests(int page) - throws HelloSignException { - Map params = new HashMap(); - params.put(AbstractResourceList.PAGE, Integer.toString(page)); - HttpGetRequest request = new HttpGetRequest( - URL_SIGNATURE_REQUEST_LIST, params, auth); - return new SignatureRequestList(request.getJsonResponse()); + public SignatureRequestList getSignatureRequests(int page) throws HelloSignException { + return new SignatureRequestList( + httpClient.withAuth(auth).withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) + .get(BASE_URI + SIGNATURE_REQUEST_LIST_URI).asJson()); } /** * Sends the provided signature request to HelloSign. + * * @param req SignatureRequest * @return SignatureRequest - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public SignatureRequest sendSignatureRequest(SignatureRequest req) - throws HelloSignException { + public SignatureRequest sendSignatureRequest(SignatureRequest req) throws HelloSignException { if (req.hasId()) { - throw new HelloSignException( - "Sending an existing signature request is not supported"); + throw new HelloSignException("Sending an existing signature request is not supported"); } - HttpPostRequest request = new HttpPostRequest( - URL_SIGNATURE_REQUEST_SEND, req.getPostFields(), auth); - JSONObject json = request.getJsonResponse(); - return new SignatureRequest(json); + return new SignatureRequest(httpClient.withAuth(auth).withPostFields(req.getPostFields()) + .post(BASE_URI + SIGNATURE_REQUEST_SEND_URI).asJson()); } /** @@ -626,211 +476,216 @@ public SignatureRequest sendSignatureRequest(SignatureRequest req) * This method requires the ID of the siganture request that has already * been sent, as well as the signature_id that represents the signer that * should be updated. The ema - * @param signature_request_id String ID of the signature request that has - * already been sent and needs to be updated. - * @param signature_id String ID of the signer that needs to be updated. - * @param new_email_address String email address that the signer should be - * changed to + * + * @param signatureRequestId String ID of the signature request that has + * already been sent and needs to be updated. + * @param signatureId String ID of the signer that needs to be updated. + * @param newEmailAddress String email address that the signer should be + * changed to * @return SignatureRequest The updated request data * @throws HelloSignException thrown if there's a problem processing the - * HTTP request or the JSON response. + * HTTP request or the JSON response. */ - public SignatureRequest updateSignatureRequest( - String signature_request_id, - String signature_id, - String new_email_address) throws HelloSignException { - String url = this.URL_SIGNATURE_REQUEST_UPDATE + "/" + signature_request_id; - Map fields = new HashMap(); - fields.put(Signature.SIGNATURE_ID, signature_id); - fields.put(SignatureRequest.SIGREQ_SIGNER_EMAIL, new_email_address); - HttpPostRequest request = new HttpPostRequest(url, fields, auth); - JSONObject json = request.getJsonResponse(); - return new SignatureRequest(json); + public SignatureRequest updateSignatureRequest(String signatureRequestId, String signatureId, + String newEmailAddress) throws HelloSignException { + String url = BASE_URI + SIGNATURE_REQUEST_UPDATE_URI + "/" + signatureRequestId; + return new SignatureRequest(httpClient.withAuth(auth).withPostField(Signature.SIGNATURE_ID, signatureId) + .withPostField(SignatureRequest.SIGREQ_SIGNER_EMAIL, newEmailAddress).post(url).asJson()); } /** * Retrieves the templates for the current user account. + * * @return TemplateList - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public TemplateList getTemplates() throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_TEMPLATE_LIST, auth); - return new TemplateList(request.getJsonResponse()); + return new TemplateList(httpClient.withAuth(auth).get(BASE_URI + TEMPLATE_LIST_URI).asJson()); } /** * Retreives a page of templates for the current user account. + * * @param page int * @return TemplateList - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public TemplateList getTemplates(int page) throws HelloSignException { - Map params = new HashMap(); - params.put(AbstractResourceList.PAGE, Integer.toString(page)); - HttpGetRequest request = new HttpGetRequest(URL_TEMPLATE_LIST, params, auth); - return new TemplateList(request.getJsonResponse()); + return new TemplateList( + httpClient.withAuth(auth).withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) + .get(BASE_URI + TEMPLATE_LIST_URI).asJson()); } - /** - * Retrieves the PDF file backing the Template specified by - * the provided Template ID. + * Retrieves the PDF file backing the Template specified by the provided + * Template ID. + * * @param templateId String Template ID * @return File PDF file object - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public File getTemplateFile(String templateId) throws HelloSignException { - String url = URL_TEMPLATE_FILE + "/" + templateId; - HttpGetRequest request = new HttpGetRequest(url, auth); - return request.getFileResponse(TEMPLATE_FILE_NAME + "." + TEMPLATE_FILE_EXT); + String url = BASE_URI + TEMPLATE_FILE_URI + "/" + templateId; + String fileName = TEMPLATE_FILE_NAME + "." + TEMPLATE_FILE_EXT; + return httpClient.withAuth(auth).get(url).asFile(fileName); + } + + /** + * Returns a signed URL that can be used to retrieve the file backing a + * template. + * + * @param templateId String Template ID + * @return String URL or null if no file URL can be retrieved + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. + */ + public String getTemplateFileUrl(String templateId) throws HelloSignException { + String fileUrl = null; + String url = BASE_URI + TEMPLATE_FILE_URI + "/" + templateId; + JSONObject response = httpClient.withAuth(auth).withGetParam(PARAM_TEMPLATE_GET_URL, "1").get(url).asJson(); + if (response.has("file_url")) { + try { + fileUrl = response.getString("file_url"); + } catch (JSONException ex) { + throw new HelloSignException(ex); + } + } + return fileUrl; } /** * Retrieves a specific Template based on the provided Template ID. + * * @param templateId String Template ID * @return Template - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public Template getTemplate(String templateId) throws HelloSignException { - String url = URL_TEMPLATE + "/" + templateId; - HttpGetRequest request = new HttpGetRequest(url, auth); - return new Template(request.getJsonResponse()); + String url = BASE_URI + TEMPLATE_URI + "/" + templateId; + return new Template(httpClient.withAuth(auth).get(url).asJson()); } /** - * Adds the provided user to the template indicated by the provided template ID. - * The new user can be designated using their account ID or email address. + * Adds the provided user to the template indicated by the provided template + * ID. The new user can be designated using their account ID or email + * address. + * * @param templateId String template ID * @param idOrEmail String account ID or email address * @return Template - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public Template addTemplateUser(String templateId, String idOrEmail) - throws HelloSignException { - String url = URL_TEMPLATE_ADD_USER + "/" + templateId; - Map fields = new HashMap(); - if (idOrEmail.contains("@")) { - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, idOrEmail); - } else { - fields.put(Account.ACCOUNT_ID, idOrEmail); - } - HttpPostRequest request = new HttpPostRequest(url, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Template(json); + public Template addTemplateUser(String templateId, String idOrEmail) throws HelloSignException { + String url = BASE_URI + TEMPLATE_ADD_USER_URI + "/" + templateId; + String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS + : Account.ACCOUNT_ID; + return new Template(httpClient.withAuth(auth).withPostField(key, idOrEmail).post(url).asJson()); } - public boolean deleteTemplate(String templateId) - throws HelloSignException { - String url = this.URL_TEMPLATE_DELETE + "/" + templateId; - HttpPostRequest request = new HttpPostRequest(url, auth); - int response = request.getHttpResponseCode(); - if (response == 200) { - return true; - } - throw new HelloSignException("Unable to delete template with ID " + templateId + ". Server returned: " + response); + /** + * Delete the template designated by the template id + * + * @param templateId String template ID + * @return true if the delete was successful, false otherwise + * @throws HelloSignException thrown if there is a problem processing the + * HTTP request + */ + public boolean deleteTemplate(String templateId) throws HelloSignException { + String url = BASE_URI + TEMPLATE_DELETE_URI + "/" + templateId; + return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth).post(url).asHttpCode(); } /** - * Adds the provided user to the template indicated by the provided template ID. - * The new user can be designated using their account ID or email address. + * Adds the provided user to the template indicated by the provided template + * ID. The new user can be designated using their account ID or email + * address. + * * @param templateId String template ID * @param idOrEmail String account ID or email address * @return Template - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public Template removeTemplateUser(String templateId, String idOrEmail) - throws HelloSignException { - String url = URL_TEMPLATE_REMOVE_USER + "/" + templateId; - Map fields = new HashMap(); - if (idOrEmail.contains("@")) { - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, idOrEmail); - } else { - fields.put(Account.ACCOUNT_ID, idOrEmail); - } - HttpPostRequest request = new HttpPostRequest(url, fields, auth); - JSONObject json = request.getJsonResponse(); - return new Template(json); + public Template removeTemplateUser(String templateId, String idOrEmail) throws HelloSignException { + String url = BASE_URI + TEMPLATE_REMOVE_USER_URI + "/" + templateId; + String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS + : Account.ACCOUNT_ID; + return new Template(httpClient.withAuth(auth).withPostField(key, idOrEmail).post(url).asJson()); } /** - * Creates a new Signature Request based on the template provided. + * Creates a new Signature Request based on the template provided. + * * @param req TemplateSignatureRequest * @return SignatureRequest - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public SignatureRequest sendTemplateSignatureRequest(TemplateSignatureRequest req) - throws HelloSignException { - HttpPostRequest request = new HttpPostRequest(URL_TEMPLATE_SIGNATURE_REQUEST, - req.getPostFields(), auth); - JSONObject json = request.getJsonResponse(); - return new SignatureRequest(json); + public SignatureRequest sendTemplateSignatureRequest(TemplateSignatureRequest req) throws HelloSignException { + return new SignatureRequest(httpClient.withAuth(auth).withPostFields(req.getPostFields()) + .post(BASE_URI + TEMPLATE_SIGNATURE_REQUEST_URI).asJson()); } /** - * Cancels an existing signature request. If it has been completed, it will delete - * the signature request from your account. + * Cancels an existing signature request. If it has been completed, it will + * delete the signature request from your account. + * * @param id SignatureRequest id - * @return HttpStatus code - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @return boolean true if successful + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public int cancelSignatureRequest(String id) throws HelloSignException { - String cancelUrl = URL_SIGNATURE_REQUEST_CANCEL + "/" + id; - HttpPostRequest request = new HttpPostRequest(cancelUrl, auth); - return request.getHttpResponseCode(); + public boolean cancelSignatureRequest(String id) throws HelloSignException { + String url = BASE_URI + SIGNATURE_REQUEST_CANCEL_URI + "/" + id; + return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth).post(url).asHttpCode(); } /** - * Instructs HelloSign to email the given address with a reminder to sign the - * SignatureRequest referenced by the given request ID. + * Instructs HelloSign to email the given address with a reminder to sign + * the SignatureRequest referenced by the given request ID. * - * Note: You cannot send a reminder within 1 hours of the last reminder that was - * sent, manually or automatically. + * Note: You cannot send a reminder within 1 hours of the last reminder that + * was sent, manually or automatically. * * @param requestId String SignatureRequest ID * @param email String email * @return SignatureRequest The request to be reminded - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public SignatureRequest requestEmailReminder(String requestId, String email) - throws HelloSignException { - Map fields = new HashMap(); - fields.put(Account.ACCOUNT_EMAIL_ADDRESS, email); - String remindUrl = URL_SIGNATURE_REQUEST_REMIND + "/" + requestId; - HttpPostRequest request = new HttpPostRequest(remindUrl, fields, auth); - JSONObject json = request.getJsonResponse(); - return new SignatureRequest(json); + public SignatureRequest requestEmailReminder(String requestId, String email) throws HelloSignException { + String url = BASE_URI + SIGNATURE_REQUEST_REMIND_URI + "/" + requestId; + return new SignatureRequest( + httpClient.withAuth(auth).withPostField(Account.ACCOUNT_EMAIL_ADDRESS, email).post(url).asJson()); } /** - * Retrieves the final PDF copy of a signature request, if it exists. + * Retrieves the final PDF copy of a signature request, if it exists. + * * @param requestId String SignatureRequest ID * @return File final copy file, or null if it does not yet exist - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. - * @deprecated Use getFiles(requestId) + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. + * @deprecated Use {{@link #getFiles(String)} */ public File getFinalCopy(String requestId) throws HelloSignException { - String finalCopyUrl = URL_SIGNATURE_REQUEST_FINAL_COPY + "/" + requestId; - String filename = FINAL_COPY_FILE_NAME + "." + FINAL_COPY_FILE_EXT; - HttpGetRequest request = new HttpGetRequest(finalCopyUrl, auth); - return request.getFileResponse(filename); + String url = BASE_URI + SIGNATURE_REQUEST_FINAL_COPY_URI + "/" + requestId; + String filename = FINAL_COPY_FILE_NAME + "." + FINAL_COPY_FILE_EXT; + return httpClient.withAuth(auth).get(url).asFile(filename); } /** * Retrieves a PDF copy of the files associated with a signature request. + * * @param requestId String signature ID * @return File PDF file - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public File getFiles(String requestId) throws HelloSignException { return getFiles(requestId, SignatureRequest.SIGREQ_FORMAT_PDF); @@ -838,50 +693,50 @@ public File getFiles(String requestId) throws HelloSignException { /** * Retrieves the file associated with a signature request. + * * @param requestId String signature ID * @param format String format, see SignatureRequest for available types * @return File - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public File getFiles(String requestId, String format) throws HelloSignException { if (format == null || format.isEmpty()) { format = FILES_FILE_EXT; } - String filesUrl = URL_SIGNATURE_REQUEST_FILES + "/" + requestId + - "?" + URL_PARAM_FILE_TYPE + "=" + format; - HttpGetRequest request = new HttpGetRequest(filesUrl, null, auth); - String filename = FILES_FILE_NAME + "." + format; - return request.getFileResponse(filename); + String url = BASE_URI + SIGNATURE_REQUEST_FILES_URI + "/" + requestId; + String fileName = FILES_FILE_NAME + "." + format; + return httpClient.withAuth(auth).withGetParam(PARAM_FILE_TYPE_URI, format).get(url).asFile(fileName); } /** * Creates a signature request that can be embedded within your website. + * * @param embeddedReq EmbeddedRequest * @return SignatureRequest - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public AbstractRequest createEmbeddedRequest(EmbeddedRequest embeddedReq) - throws HelloSignException { - String url = URL_SIGNATURE_REQUEST_EMBEDDED; + public AbstractRequest createEmbeddedRequest(EmbeddedRequest embeddedReq) throws HelloSignException { + String url = BASE_URI; Class returnType = SignatureRequest.class; AbstractRequest req = embeddedReq.getRequest(); if (req instanceof TemplateSignatureRequest) { - url = URL_SIGNATURE_REQUEST_EMBEDDED_TEMPLATE; + url += SIGNATURE_REQUEST_EMBEDDED_TEMPLATE_URI; } else if (req instanceof UnclaimedDraft) { if (((UnclaimedDraft) req).getRequest() instanceof TemplateSignatureRequest) { - url = URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE; + url += UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE_URI; } else { - url = URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED; + url += UNCLAIMED_DRAFT_CREATE_EMBEDDED_URI; } returnType = UnclaimedDraft.class; + } else { + url += SIGNATURE_REQUEST_EMBEDDED_URI; } - HttpPostRequest request = new HttpPostRequest(url, embeddedReq.getPostFields(), auth); - JSONObject json = request.getJsonResponse(); try { Constructor constructor = returnType.getConstructor(JSONObject.class); - return (AbstractRequest) constructor.newInstance(json); + return (AbstractRequest) constructor.newInstance( + httpClient.withAuth(auth).withPostFields(embeddedReq.getPostFields()).post(url).asJson()); } catch (Exception ex) { throw new HelloSignException(ex); } @@ -889,245 +744,199 @@ public AbstractRequest createEmbeddedRequest(EmbeddedRequest embeddedReq) /** * Retrieves the necessary information to build an embedded signature - * request. + * request. + * * @param signatureId String ID of the signature request to embed * @return EmbeddedResponse - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public EmbeddedResponse getEmbeddedSignUrl(String signatureId) - throws HelloSignException { - String url = URL_EMBEDDED_SIGN_URL + "/" + signatureId; - HttpGetRequest request = new HttpGetRequest(url, auth); - JSONObject json = request.getJsonResponse(); - return new EmbeddedResponse(json); + public EmbeddedResponse getEmbeddedSignUrl(String signatureId) throws HelloSignException { + String url = BASE_URI + EMBEDDED_SIGN_URL_URI + "/" + signatureId; + return new EmbeddedResponse(httpClient.withAuth(auth).post(url).asJson()); } /** * Retrieves the necessary information to edit an embedded template. + * * @param templateId String ID of the signature request to embed * @return EmbeddedResponse - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId) - throws HelloSignException { - return getEmbeddedTemplateEditUrl(templateId, false, false); + public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId) throws HelloSignException { + return getEmbeddedTemplateEditUrl(templateId, false, false, false); } /** * Retrieves the necessary information to edit an embedded template. + * * @param templateId String ID of the signature request to embed - * @param skipSignerRoles true if the edited template should not allow - * the user to modify the template's signer roles. Defaults to false. - * @param skipSubjectMessage true if the edited template should - * not allow the user to modify the template's subject and message. - * Defaults to false. + * @param skipSignerRoles true if the edited template should not allow the + * user to modify the template's signer roles. Defaults to false. + * @param skipSubjectMessage true if the edited template should not allow + * the user to modify the template's subject and message. Defaults to + * false. * @return EmbeddedResponse - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId, boolean skipSignerRoles, boolean skipSubjectMessage) - throws HelloSignException { - String url = this.URL_EMBEDDED_EDIT_URL + "/" + templateId; - if (skipSignerRoles || skipSubjectMessage) { - url += '?'; - if (skipSignerRoles) { - url += "skip_signer_roles=1"; - } - if (skipSubjectMessage) { - url += "skip_subject_message=1"; - } - } - HttpPostRequest request = new HttpPostRequest(url, auth); - JSONObject json = request.getJsonResponse(); - return new EmbeddedResponse(json); + public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId, boolean skipSignerRoles, + boolean skipSubjectMessage) throws HelloSignException { + return getEmbeddedTemplateEditUrl(templateId, skipSignerRoles, skipSubjectMessage, false); + } + + /** + * Retrieves the necessary information to edit an embedded template. + * + * @param templateId String ID of the signature request to embed + * @param skipSignerRoles true if the edited template should not allow the + * user to modify the template's signer roles. Defaults to false. + * @param skipSubjectMessage true if the edited template should not allow + * the user to modify the template's subject and message. Defaults to + * false. + * @param testMode true if this request is a test request. Useful for + * editing locked templates. + * @return EmbeddedResponse + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. + */ + public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId, boolean skipSignerRoles, + boolean skipSubjectMessage, boolean testMode) throws HelloSignException { + String url = BASE_URI + EMBEDDED_EDIT_URL_URI + "/" + templateId; + return new EmbeddedResponse( + httpClient.withAuth(auth).withPostField(EMBEDDED_TEMPLATE_SKIP_SIGNER_ROLES, skipSignerRoles) + .withPostField(EMBEDDED_TEMPLATE_SKIP_SUBJECT_MESSAGE, skipSubjectMessage) + .withPostField(AbstractRequest.REQUEST_TEST_MODE, testMode).post(url).asJson()); } /** * Creates an unclaimed draft using the provided request draft object. + * * @param draft UnclaimedDraft * @return UnclaimedDraft The created draft - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public UnclaimedDraft createUnclaimedDraft(UnclaimedDraft draft) - throws HelloSignException { - String url = URL_UNCLAIMED_DRAFT_CREATE; + public UnclaimedDraft createUnclaimedDraft(UnclaimedDraft draft) throws HelloSignException { + String url = BASE_URI; if (draft.isForEmbeddedSigning()) { - url = URL_UNCLAIMED_DRAFT_CREATE_EMBEDDED; + url += UNCLAIMED_DRAFT_CREATE_EMBEDDED_URI; + } else { + url += UNCLAIMED_DRAFT_CREATE_URI; } - HttpPostRequest request = new HttpPostRequest(url, draft.getPostFields(), auth); - JSONObject json = request.getJsonResponse(); - return new UnclaimedDraft(json); + return new UnclaimedDraft(httpClient.withAuth(auth).withPostFields(draft.getPostFields()).post(url).asJson()); } /** * Creates a template draft that can be used for embedded template creation. + * * @param req EmbeddedRequest * @return Template the unclaimed template draft - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ - public TemplateDraft createEmbeddedTemplateDraft(EmbeddedRequest req) - throws HelloSignException { - String url = URL_TEMPLATE_CREATE_EMBEDDED_DRAFT; - Class returnType = TemplateDraft.class; - HttpPostRequest request = new HttpPostRequest(url, req.getPostFields(), auth); - JSONObject json = request.getJsonResponse(); - try { - Constructor constructor = returnType.getConstructor(JSONObject.class); - return (TemplateDraft) constructor.newInstance(json); - } catch (Exception ex) { - throw new HelloSignException(ex); - } + public TemplateDraft createEmbeddedTemplateDraft(EmbeddedRequest req) throws HelloSignException { + return new TemplateDraft(httpClient.withAuth(auth).withPostFields(req.getPostFields()) + .post(BASE_URI + TEMPLATE_CREATE_EMBEDDED_DRAFT_URI).asJson()); } /** * Retrieves the API app configuration for the given Client ID. + * * @param clientId String * @return ApiApp - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public ApiApp getApiApp(String clientId) throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_API_APP + "/" + clientId, auth); - return new ApiApp(request.getJsonResponse()); + String url = BASE_URI + API_APP_URI + "/" + clientId; + return new ApiApp(httpClient.withAuth(auth).get(url).asJson()); } /** * Retrieves a paged list of API apps for the authenticated account. + * * @return ApiAppList - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public ApiAppList getApiApps() throws HelloSignException { - HttpGetRequest request = new HttpGetRequest(URL_API_APP_LIST, auth); - return new ApiAppList(request.getJsonResponse()); + return new ApiAppList(httpClient.withAuth(auth).get(BASE_URI + API_APP_LIST_URI).asJson()); } /** * Creates a new ApiApp using the properties set on the provided ApiApp. + * * @param app ApiApp * @return ApiApp newly created ApiApp - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public ApiApp createApiApp(ApiApp app) throws HelloSignException { - HttpPostRequest request = new HttpPostRequest(URL_API_APP, app.getPostFields(), auth); - return new ApiApp(request.getJsonResponse()); + return new ApiApp( + httpClient.withAuth(auth).withPostFields(app.getPostFields()).post(BASE_URI + API_APP_URI).asJson()); } /** * Attempts to delete the API app with the given client ID. + * * @param clientId String The Client ID of the app that should be deleted. * @return boolean true if the API app was successfully deleted - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public boolean deleteApiApp(String clientId) throws HelloSignException { - HttpDeleteRequest request = new HttpDeleteRequest(URL_API_APP + "/" + clientId, auth); - return HttpURLConnection.HTTP_NO_CONTENT == request.getHttpResponseCode(); + String url = API_APP_URI + "/" + clientId; + return HttpURLConnection.HTTP_NO_CONTENT == httpClient.withAuth(auth).delete(url).asHttpCode(); } /** * Updates the API app with the given ApiApp object properties. + * * @param app ApiApp * @return ApiApp updated ApiApp - * @throws HelloSignException thrown if there's a problem processing - * the HTTP request or the JSON response. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or the JSON response. */ public ApiApp updateApiApp(ApiApp app) throws HelloSignException { if (!app.hasClientId()) { throw new HelloSignException("Cannot update an ApiApp without a client ID. Create one first!"); } - HttpPutRequest request = new HttpPutRequest(URL_API_APP + "/" + app.getClientId(), app.getPostFields(), auth); - return new ApiApp(request.getJsonResponse()); + String url = API_APP_URI + "/" + app.getClientId(); + return new ApiApp(httpClient.withAuth(auth).withPostFields(app.getPostFields()).put(url).asJson()); } /** - * Performs a simple call to the HelloSign API to see if it's available - * with the given credentials. - * @return true if HelloSign is available and the client is online, - * false otherwise. + * Performs an OPTIONS call to the HelloSign API to see if it's online. + * + * @return true if HelloSign is available and the client is online, false + * otherwise. + * @throws HelloSignException thrown if there's a problem processing the + * HTTP request or response. */ - public boolean isOnline() { - HttpURLConnection connection = null; + public boolean isOnline() throws HelloSignException { + boolean isOnline = false; try { - connection = AbstractHttpRequest.getConnection(URL_API); - connection.setRequestMethod("OPTIONS"); - return HttpURLConnection.HTTP_OK == connection.getResponseCode(); + isOnline = (httpClient.options(BASE_URI).asHttpCode() == HttpURLConnection.HTTP_OK); + } catch (HelloSignException ex) { + throw ex; } catch (Exception ex) { - // Ignore + throw new HelloSignException(ex); } - return false; + return isOnline; } /** * Sets the access token for the OAuth user that this client will use to * perform requests. + * * @param accessToken String access token * @param tokenType String token type - * @throws HelloSignException thrown if there's a problem setting the access token. + * @throws HelloSignException thrown if there's a problem setting the access + * token. */ - public void setAccessToken(String accessToken, String tokenType) - throws HelloSignException { + public void setAccessToken(String accessToken, String tokenType) throws HelloSignException { auth.setAccessToken(accessToken, tokenType); } - - // Utility methods to assist in testing, since we are dynamically configuring these URLs - // based on a system property ("hellosign.env"). - public String getApiUrl() { - return URL_API; - } - public String getSignatureRequestUrl() { - return URL_SIGNATURE_REQUEST; - } - public String getSignatureRequestSendUrl() { - return URL_SIGNATURE_REQUEST_SEND; - } - public String getSignatureRequestCancelUrl() { - return URL_SIGNATURE_REQUEST_CANCEL; - } - public String getTemplateSignatureRequestUrl() { - return URL_TEMPLATE_SIGNATURE_REQUEST; - } - - // ======================================================================= - // Static helpers - // ======================================================================= - private static void disableStrictSSL() { - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - public void checkClientTrusted(X509Certificate[] certs, - String authType) { - } - - public void checkServerTrusted(X509Certificate[] certs, - String authType) { - } - } }; - - // Ignore differences between given hostname and certificate hostname - HostnameVerifier hv = new HostnameVerifier() { - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; - - // Install the all-trusting trust manager - try { - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, trustAllCerts, new SecureRandom()); - HttpsURLConnection - .setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier(hv); - } catch (Exception e) { - } - } - } diff --git a/src/main/java/com/hellosign/sdk/HelloSignException.java b/src/main/java/com/hellosign/sdk/HelloSignException.java index 49badd2c..81b5335d 100644 --- a/src/main/java/com/hellosign/sdk/HelloSignException.java +++ b/src/main/java/com/hellosign/sdk/HelloSignException.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -26,8 +26,8 @@ /** * This class wraps all hellosign-java-sdk exceptions. This allows a developer - * to determine where the exception is coming from. It also allows us to - * store the HTTP code and error type from API calls. + * to determine where the exception is coming from. It also allows us to store + * the HTTP code and error type from API calls. * * @author "Chris Paul (chris@hellosign.com)" */ @@ -61,8 +61,9 @@ public HelloSignException(String message, Integer httpCode, String type, Excepti } /** - * Returns the HTTP code associated with a HelloSign API call. - * This may be null if the exception does not involve an API request. + * Returns the HTTP code associated with a HelloSign API call. This may be + * null if the exception does not involve an API request. + * * @return Integer or null if an HTTP code does not exist */ public Integer getHttpCode() { @@ -70,8 +71,9 @@ public Integer getHttpCode() { } /** - * Returns the HelloSign API error type. This may be null if the - * exception does not involve an API request. + * Returns the HelloSign API error type. This may be null if the exception + * does not involve an API request. + * * @return String or null if the error type does not exist */ public String getType() { diff --git a/src/main/java/com/hellosign/sdk/http/AbstractHttpRequest.java b/src/main/java/com/hellosign/sdk/http/AbstractHttpRequest.java index 1bdd1446..bddc8af6 100644 --- a/src/main/java/com/hellosign/sdk/http/AbstractHttpRequest.java +++ b/src/main/java/com/hellosign/sdk/http/AbstractHttpRequest.java @@ -1,5 +1,31 @@ package com.hellosign.sdk.http; +/** + * The MIT License (MIT) + * + * Copyright (C) 2017 hellosign.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import java.io.File; + /** * The MIT License (MIT) * @@ -12,8 +38,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -32,19 +58,31 @@ import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.Properties; import java.util.Scanner; -import org.json.JSONException; -import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hellosign.sdk.HelloSignException; public abstract class AbstractHttpRequest { + private static final Logger logger = LoggerFactory.getLogger(AbstractHttpRequest.class); + public final static String DEFAULT_ENCODING = "UTF-8"; public final static String USER_AGENT = createUserAgent(); + // Request variables + protected String url; + protected Authentication auth; + + // Response variables + protected Integer lastHttpStatusCode; + protected InputStream lastResponseStream; + private static String createUserAgent() { String filename = "config.properties"; Properties props = new Properties(); @@ -65,44 +103,72 @@ private static String createUserAgent() { return "hellosign-java-sdk/" + version; } - protected String url; - protected Authentication auth; + /** + * Executes this HTTP request and preserves the response stream and HTTP + * response code for processing. + * + * @throws HelloSignException Thrown if there is an error while making the + * HTTP request to the HelloSign API. + */ + public void execute() throws HelloSignException { + HttpURLConnection connection = getConnection(); + try { + // Execute the request and save the HTTP status code + lastHttpStatusCode = connection.getResponseCode(); - public static String convertStreamToString(InputStream in) { - Scanner s = new Scanner(in); - s.useDelimiter("\\A"); - String result = (s.hasNext()) ? s.next() : ""; - s.close(); - return result; + // Save the stream object for processing + if (lastHttpStatusCode >= 200 && lastHttpStatusCode < 300) { + logger.debug("OK!"); + lastResponseStream = connection.getInputStream(); + } else { + logger.error("Error! HTTP Code = " + lastHttpStatusCode); + lastResponseStream = connection.getErrorStream(); + } + } catch (Exception ex) { + throw new HelloSignException(ex); + } } - protected static void validate(JSONObject json, int code) throws HelloSignException { - if (json.has("error")) { - try { - JSONObject error = json.getJSONObject("error"); - String message = error.getString("error_msg"); - String type = error.getString("error_name"); - throw new HelloSignException(message, code, type); - } catch (JSONException ex) { - throw new HelloSignException(ex); - } + /** + * Returns the last HTTP response code. + * + * @return Integer response code + */ + public Integer getResponseCode() { + return lastHttpStatusCode; + } + + /** + * Returns the last response stream as a string. + * + * @return String + */ + public String getResponseBody() { + String responseStr = ""; + if (lastResponseStream == null) { + logger.error("Unable to parse JSON from empty response!"); + } else { + Scanner s = new Scanner(lastResponseStream); + s.useDelimiter("\\A"); + responseStr = (s.hasNext()) ? s.next() : ""; + s.close(); } + return responseStr; } /** * Creates an HTTP connection. * * Optionally checks for proxy parameters and creates a proxied connection - * using the system properties: - * "hellosign.proxy.url" - the URL of the HTTP proxy - * "hellosign.proxy.port" - the port of the HTTP proxy + * using the system properties: "hellosign.proxy.url" - the URL of the HTTP + * proxy "hellosign.proxy.port" - the port of the HTTP proxy * * @param url String URL to connect to * @return HttpUrlConnection the (proxied) connection to the URL * @throws MalformedURLException thrown if the URL is invalid * @throws IOException thrown if IO cannot be established with the URL */ - public static HttpURLConnection getConnection(String url) throws MalformedURLException, IOException { + protected static HttpURLConnection getProxiedConnection(String url) throws MalformedURLException, IOException { HttpURLConnection conn = null; Proxy proxy = null; String proxyUrlStr = System.getProperty("hellosign.proxy.url"); @@ -115,10 +181,37 @@ public static HttpURLConnection getConnection(String url) throws MalformedURLExc proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyUrlStr, proxyPort)); } if (proxy == null) { - conn = (HttpURLConnection) new URL(url).openConnection(); + conn = (HttpURLConnection) new URL(url).openConnection(); } else { conn = (HttpURLConnection) new URL(url).openConnection(proxy); } return conn; } + + /** + * The method class will create the appropriate connection with an endpoint, + * parameters, etc. + * + * @return HttpURLConnection + * @throws HelloSignException Thrown if a connection cannot be created + */ + abstract protected HttpURLConnection getConnection() throws HelloSignException; + + /** + * Write the last response to a file. + * + * @param f File + * @return long bytes written + * @throws HelloSignException Thrown if an exception occurs during the copy + * of the response stream to the given file. + */ + public long getResponseAsFile(File f) throws HelloSignException { + long bytesWritten = 0; + try { + bytesWritten = Files.copy(lastResponseStream, f.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + throw new HelloSignException(e); + } + return bytesWritten; + } } diff --git a/src/main/java/com/hellosign/sdk/http/Authentication.java b/src/main/java/com/hellosign/sdk/http/Authentication.java index 261fe2a4..f370f0ff 100644 --- a/src/main/java/com/hellosign/sdk/http/Authentication.java +++ b/src/main/java/com/hellosign/sdk/http/Authentication.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -35,9 +35,9 @@ /** * This class provides convenience methods for handling authentication - * information for a HelloSignClient instance. We usually only want to use - * one of these methods, but don't necessarily want to have to pass around - * all of the information in method parameters. + * information for a HelloSignClient instance. We usually only want to use one + * of these methods, but don't necessarily want to have to pass around all of + * the information in method parameters. * * @author Chris Paul (chris@hellosign.com) */ @@ -45,23 +45,19 @@ public class Authentication { private static final Logger logger = LoggerFactory.getLogger(Authentication.class); - private String email = new String(); - private String password = new String(); private String apiKey = new String(); private String accessToken = new String(); private String accessTokenType = new String(); - private static final String[] allowedOauthOps = { - "account", "signature_request", "reusable_form", "template" - }; + private static final String[] allowedOauthOps = { "account", "signature_request", "reusable_form", "template" }; - public Authentication() {} + public Authentication() {} - public Authentication (Authentication clone) - throws HelloSignException { - if (clone.hasWebsiteCredentials()) { - setWebsiteCredentials(clone.getEmail(), clone.getPassword()); - } + public Authentication(String apiKey) { + this.apiKey = apiKey; + } + + public Authentication(Authentication clone) throws HelloSignException { if (clone.hasApiKey()) { setApiKey(clone.getApiKey()); } @@ -70,52 +66,9 @@ public Authentication (Authentication clone) } } - /** - * Sets the email and password to use for authenticating the - * client. - * @param email String email address - * @param password String password - * @throws HelloSignException thrown if either the email or password - * are null - */ - public void setWebsiteCredentials(String email, String password) - throws HelloSignException { - if (email == null) { - throw new HelloSignException("Email cannot be null"); - } - if (password == null) { - throw new HelloSignException("Password cannot be null"); - } - this.email = new String(email); - this.password = new String(password); - } - - /** - * Returns the email address for this client. - * @return String email - */ - public String getEmail() { - return email; - } - - /** - * Returns the password for this client. - * @return String password - */ - public String getPassword() { - return password; - } - - /** - * Returns true if an email and password have been set. - * @return true, or false if either are empty - */ - public boolean hasWebsiteCredentials() { - return !("".equals(email) || "".equals(password)); - } - /** * Returns a protected copy of the API key String. + * * @return String API key */ public String getApiKey() { @@ -124,6 +77,7 @@ public String getApiKey() { /** * Sets the API key to use for authenticating this client. + * * @param apiKey String API Key */ public void setApiKey(String apiKey) { @@ -135,6 +89,7 @@ public void setApiKey(String apiKey) { /** * Returns true if the API Key has been set. + * * @return true, or false if the key is empty */ public boolean hasApiKey() { @@ -143,6 +98,7 @@ public boolean hasApiKey() { /** * Returns a protected copy of the access token. + * * @return String access token */ public String getAccessToken() { @@ -151,6 +107,7 @@ public String getAccessToken() { /** * Returns a protected copy of the access token type string. + * * @return String access token type */ public String getAccessTokenType() { @@ -159,13 +116,13 @@ public String getAccessTokenType() { /** * Sets the access token for the HelloSign client authentication. + * * @param accessToken String * @param tokenType String - * @throws HelloSignException - * if either the accessToken or tokenType are null + * @throws HelloSignException if either the accessToken or tokenType are + * null */ - public void setAccessToken(String accessToken, String tokenType) - throws HelloSignException { + public void setAccessToken(String accessToken, String tokenType) throws HelloSignException { if (accessToken == null) { throw new HelloSignException("Access Token cannot be null"); } @@ -178,6 +135,7 @@ public void setAccessToken(String accessToken, String tokenType) /** * Returns true if an access token and token type have been provided. + * * @return true or false if either are not set */ public boolean hasAccessToken() { @@ -195,19 +153,19 @@ private boolean isOperationOauth(String url) { /** * Authorizes the HTTP connection using this instance's credentials. + * * @param httpConn HttpURLConnection to be authenticated * @param url String URL against which this connection should be authorized. */ public void authenticate(HttpURLConnection httpConn, String url) { String authorization = null; if (hasAccessToken() && isOperationOauth(url)) { + logger.debug("Using OAuth token to authenticate"); authorization = getAccessTokenType() + " " + getAccessToken(); } else if (hasApiKey()) { + logger.debug("Using API Key to authenticate"); String apiKey = getApiKey() + ":"; authorization = "Basic " + DatatypeConverter.printBase64Binary(apiKey.getBytes()).trim(); - } else if (hasWebsiteCredentials()) { - String authStr = getEmail() + ":" + getPassword(); - authorization = "Basic " + DatatypeConverter.printBase64Binary(authStr.getBytes()).trim(); } if (authorization != null) { logger.debug("Authorization: " + authorization); diff --git a/src/main/java/com/hellosign/sdk/http/HttpClient.java b/src/main/java/com/hellosign/sdk/http/HttpClient.java new file mode 100644 index 00000000..130e55ef --- /dev/null +++ b/src/main/java/com/hellosign/sdk/http/HttpClient.java @@ -0,0 +1,379 @@ +package com.hellosign.sdk.http; + +/** + * The MIT License (MIT) + * + * Copyright (C) 2017 hellosign.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import java.io.File; +import java.io.Serializable; +import java.net.HttpURLConnection; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.HashMap; + +/** + * The MIT License (MIT) + * + * Copyright (C) 2016 hellosign.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import java.util.Map; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.hellosign.sdk.HelloSignException; + +/** + * Abstracts HTTP requests. + * + * @author cpaul + */ +public class HttpClient { + + private static final Logger logger = LoggerFactory.getLogger(HttpClient.class); + + private Authentication auth; + private Map getParams; + private Map postFields; + private AbstractHttpRequest request; + + public HttpClient() { + String disableSslCheck = System.getProperty("hellosign.disable.ssl"); + if (disableSslCheck != null && "true".equalsIgnoreCase(disableSslCheck)) { + disableStrictSSL(); + } + } + + public HttpClient withAuth(Authentication auth) { + this.auth = auth; + return this; + } + + public HttpClient withGetParam(String key, String value) { + if (getParams == null) { + getParams = new HashMap(); + } + getParams.put(key, value); + return this; + } + + public HttpClient withGetParams(Map params) { + getParams = params; + return this; + } + + public HttpClient withPostField(String key, Serializable value) { + if (postFields == null) { + postFields = new HashMap(); + } + postFields.put(key, value); + return this; + } + + public HttpClient withPostFields(Map fields) { + this.postFields = fields; + return this; + } + + /** + * Returns the response to the last HTTP request as a string. + * + * @return String or null if the request has not been created + */ + public String getLastResponse() { + if (request == null) { + return null; + } + return request.getResponseBody(); + } + + /** + * Returns the HTTP status code for the last request. + * + * @return Integer or null if the request has not been created + */ + public Integer getLastResponseCode() { + if (request == null) { + return null; + } + return request.getResponseCode(); + } + + /** + * Returns the last HTTP request body as a file. + * + * @param f File that should contain the response + * @return long bytes written + * @throws HelloSignException thrown if there is a problem writing to the + * file or reading the response stream + */ + public long getLastResponseAsFile(File f) throws HelloSignException { + return request.getResponseAsFile(f); + } + + /** + * Inspects the JSONObject response for errors and throws an exception if + * found. + * + * @param json JSONObject response + * @param code HTTP response code + * @throws HelloSignException thrown if an error is reported from the API + * call + */ + private void validate(JSONObject json) throws HelloSignException { + if (json.has("error")) { + try { + JSONObject error = json.getJSONObject("error"); + String message = error.getString("error_msg"); + String type = error.getString("error_name"); + throw new HelloSignException(message, getLastResponseCode(), type); + } catch (JSONException ex) { + throw new HelloSignException(ex); + } + } + } + + /** + * Clears the client after a request has successfully completed. + */ + private void reset() { + this.auth = null; + this.getParams = null; + this.postFields = null; + this.request = null; + } + + /** + * Executes the request and returns the response as a JSONObject. + * + * @return JSONObject response + * @throws HelloSignException thrown if there is a problem executing the + * request + */ + public JSONObject asJson() throws HelloSignException { + JSONObject json = null; + try { + json = new JSONObject(getLastResponse()); + } catch (JSONException e) { + throw new HelloSignException(e); + } + validate(json); + reset(); + return json; + } + + /** + * Executes the request and returns the response as a File. + * + * @param fileName String name of destination file + * @return File response + * @throws HelloSignException thrown if there is a problem executing the + * request + */ + public File asFile(String fileName) throws HelloSignException { + Integer lastResponseCode = getLastResponseCode(); + if (lastResponseCode != null && lastResponseCode != HttpURLConnection.HTTP_OK) { + this.asJson(); // Will validate response + } + File f = createTemporaryFile(fileName); + if (getLastResponseAsFile(f) == 0) { + logger.warn("No bytes written to file: " + fileName); + } + reset(); + return f; + } + + /** + * Helper method to create a temporary file. + * + * @param filename String + * @return File temporary file handle + * @throws HelloSignException thrown if the file cannot be created + */ + private File createTemporaryFile(String filename) throws HelloSignException { + String prefix = filename.substring(0, filename.indexOf(".")); + String postfix = filename.substring(filename.indexOf(".") + 1, filename.length()); + if (prefix == null || postfix == null) { + throw new HelloSignException("Invalid file name: " + prefix + "." + postfix); + } + File f = null; + try { + f = File.createTempFile(prefix, "." + postfix); + } catch (Exception ex) { + throw new HelloSignException(ex); + } + return f; + } + + /** + * Executes the request and returns the HTTP response code. + * + * @return int HTTP response code + * @throws HelloSignException thrown if no request has been performed + */ + public int asHttpCode() throws HelloSignException { + Integer code = getLastResponseCode(); + if (code == null) { + throw new HelloSignException("No request performed"); + } + if (code >= 200 && code < 300) { + reset(); + return code; + } + throw new HelloSignException("HTTP Code " + code); + } + + /** + * Initializes a GET request to the given URL. + * + * @param url String url + * @return HttpClient + * @throws HelloSignException thrown if the url is invalid + */ + public HttpClient get(String url) throws HelloSignException { + if (postFields != null) { + logger.warn("POST fields set for a GET request, they will be ignored"); + } + request = new HttpGetRequest(url, getParams, auth); + request.execute(); + return this; + } + + /** + * Initializes a POST request to the given URL. + * + * @param url String url + * @return HttpClient + * @throws HelloSignException thrown if the url is invalid + */ + public HttpClient post(String url) throws HelloSignException { + if (getParams != null) { + logger.warn("GET parameters set for a POST request, they will be ignored"); + } + request = new HttpPostRequest(url, postFields, auth); + request.execute(); + return this; + } + + /** + * Initializes a DELETE request to the given URL. + * + * @param url String url + * @return HttpClient + * @throws HelloSignException thrown if the url is invalid + */ + public HttpClient delete(String url) throws HelloSignException { + request = new HttpDeleteRequest(url, auth); + request.execute(); + return this; + } + + /** + * Makes a PUT request to the given URL + * + * @param url String url + * @return HttpClient + * @throws HelloSignException thrown if the url is invalid + */ + public HttpClient put(String url) throws HelloSignException { + request = new HttpPutRequest(url, postFields, auth); + request.execute(); + return this; + } + + /** + * Makes an OPTIONS request to the given URL + * + * @param url String URL + * @return HttpClient + * @throws HelloSignException thrown if the URL is invalid + */ + public HttpClient options(String url) throws HelloSignException { + request = new HttpOptionsRequest(url); + request.execute(); + return this; + } + + /** + * Helper method that allows this client to ignore SSL certificates when + * making API requests. + */ + private void disableStrictSSL() { + // Create a trust manager that does not validate certificate chains + TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } }; + + // Ignore differences between given hostname and certificate hostname + HostnameVerifier hv = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + // Install the all-trusting trust manager + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier(hv); + } catch (Exception e) { + } + } +} diff --git a/src/main/java/com/hellosign/sdk/http/HttpDeleteRequest.java b/src/main/java/com/hellosign/sdk/http/HttpDeleteRequest.java index 222ae7ee..c7c6e860 100644 --- a/src/main/java/com/hellosign/sdk/http/HttpDeleteRequest.java +++ b/src/main/java/com/hellosign/sdk/http/HttpDeleteRequest.java @@ -2,21 +2,16 @@ import java.net.HttpURLConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.hellosign.sdk.HelloSignException; public class HttpDeleteRequest extends AbstractHttpRequest { - private static final Logger logger = LoggerFactory.getLogger(HttpDeleteRequest.class); - public HttpDeleteRequest(String url) throws HelloSignException { this(url, null); } public HttpDeleteRequest(String url, Authentication auth) throws HelloSignException { - if (url == null || "".equals(url)) { + if (url == null || url.isEmpty()) { throw new HelloSignException("URL cannot be null or empty"); } this.url = url; @@ -25,23 +20,22 @@ public HttpDeleteRequest(String url, Authentication auth) throws HelloSignExcept } } - public int getHttpResponseCode() throws HelloSignException { + @Override + protected HttpURLConnection getConnection() throws HelloSignException { + HttpURLConnection connection; try { - logger.debug("DELETE: " + url); - HttpURLConnection connection = getConnection(url); + connection = getProxiedConnection(url); connection.setRequestProperty("Accept-Charset", DEFAULT_ENCODING); connection.setRequestProperty("user-agent", USER_AGENT); if (auth != null) { - logger.debug("Authenticating..."); auth.authenticate(connection, url); } connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestMethod("DELETE"); - connection.connect(); - return connection.getResponseCode(); - } catch (Exception e) { - throw new HelloSignException(e); + } catch (Exception ex) { + throw new HelloSignException(ex); } + return connection; } } diff --git a/src/main/java/com/hellosign/sdk/http/HttpGetRequest.java b/src/main/java/com/hellosign/sdk/http/HttpGetRequest.java index 10e48669..76021872 100644 --- a/src/main/java/com/hellosign/sdk/http/HttpGetRequest.java +++ b/src/main/java/com/hellosign/sdk/http/HttpGetRequest.java @@ -3,7 +3,7 @@ /** * The MIT License (MIT) * - * Copyright (C) 2015 hellosign.com + * Copyright (C) 2017 hellosign.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -24,21 +24,12 @@ * SOFTWARE. */ -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URLEncoder; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.Iterator; import java.util.Map; -import org.json.JSONException; -import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,49 +43,48 @@ public class HttpGetRequest extends AbstractHttpRequest { /** * Constructor + * * @param url String - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpGetRequest(String url) - throws HelloSignException { + public HttpGetRequest(String url) throws HelloSignException { this(url, null, null); } /** * Constructor + * * @param url String * @param auth Authentication - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpGetRequest(String url, Authentication auth) - throws HelloSignException { + public HttpGetRequest(String url, Authentication auth) throws HelloSignException { this(url, null, auth); } /** * Constructor + * * @param url String * @param parameters Map - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpGetRequest(String url, Map parameters) - throws HelloSignException { + public HttpGetRequest(String url, Map parameters) throws HelloSignException { this(url, parameters, null); } /** * Constructor + * * @param url String * @param parameters Map * @param auth Authentication - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown the URL is empty */ - public HttpGetRequest(String url, Map parameters, Authentication auth) - throws HelloSignException { + public HttpGetRequest(String url, Map parameters, Authentication auth) throws HelloSignException { if (url == null || "".equals(url)) { throw new HelloSignException("URL cannot be null or empty"); } @@ -108,100 +98,34 @@ public HttpGetRequest(String url, Map parameters, Authentication } /** - * Executes the GET request and converts the response to a JSON - * object. - * @return JSONObject - * @throws HelloSignException thrown if there is a problem making - * the HTTP request. + * @return HttpURLConnection used to make the HTTP request + * @see AbstractHttpRequest#getConnection() */ - public JSONObject getJsonResponse() throws HelloSignException { - JSONObject json = null; - try { - HttpURLConnection connection = get(url, parameters, auth); - int httpCode = connection.getResponseCode(); - InputStream is = null; - if (HttpURLConnection.HTTP_OK == httpCode) { - is = connection.getInputStream(); - } else { - is = connection.getErrorStream(); - } - String response = convertStreamToString(is); - json = new JSONObject(response); - validate(json, httpCode); - } catch (HelloSignException e) { - throw e; - } catch (Exception e) { - throw new HelloSignException(e); - } - return json; - } - - /** - * Execute the GET request and process the response as a file. The file - * will be stored as a temporary file with the given filename, but using - * the File.createTemporaryFile() method. This file will append a - * timestamp to the filename. - * @param filename String - * @return File - * @throws HelloSignException thrown if there is a problem creating the file - */ - public File getFileResponse(String filename) throws HelloSignException { - File f = createTemporaryFile(filename); - return getFile(url, auth, f); - } - - /** - * Helper method to create a temporary file. - * @param filename String - * @return File - * @throws HelloSignException thrown if the file cannot be created - */ - private static File createTemporaryFile(String filename) - throws HelloSignException { - String prefix = filename.substring(0, filename.indexOf(".")); - String postfix = filename.substring(filename.indexOf(".") + 1, filename.length()); - if (prefix == null || postfix == null) { - throw new HelloSignException("Invalid file name: " + prefix + "." + postfix); - } - File f = null; - try { - f = File.createTempFile(prefix, "." + postfix); - } catch (IOException ex) { - ex.printStackTrace(); - throw new HelloSignException(ex.getMessage()); - } - return f; - } - - /** - * Helper method to execute an HTTP GET request. - * @param url String - * @param parameters Map - * @param auth Authentication - * @return HttpURLConnection - * @throws UnsupportedEncodingException thrown if the encoding is not supported - * @throws IOException thrown if there is a problem handling the request - * @throws MalformedURLException thrown if the URL is unrecognizable - */ - private static HttpURLConnection get(String url, - Map parameters, Authentication auth) - throws UnsupportedEncodingException, IOException, - MalformedURLException { + @Override + protected HttpURLConnection getConnection() throws HelloSignException { if (parameters != null) { url += "?"; Iterator keys = parameters.keySet().iterator(); while (keys.hasNext()) { String key = keys.next(); - url += URLEncoder.encode(key, DEFAULT_ENCODING) - + "=" + - URLEncoder.encode(parameters.get(key), DEFAULT_ENCODING); + try { + url += URLEncoder.encode(key, DEFAULT_ENCODING) + "=" + + URLEncoder.encode(parameters.get(key), DEFAULT_ENCODING); + } catch (UnsupportedEncodingException ex) { + throw new HelloSignException(ex); + } if (keys.hasNext()) { url += "&"; } } } logger.debug("GET: " + url); - HttpURLConnection connection = getConnection(url); + HttpURLConnection connection; + try { + connection = getProxiedConnection(url); + } catch (Exception e) { + throw new HelloSignException(e); + } connection.setRequestProperty("Accept-Charset", DEFAULT_ENCODING); connection.setRequestProperty("user-agent", USER_AGENT); if (auth != null) { @@ -210,61 +134,4 @@ private static HttpURLConnection get(String url, } return connection; } - - /** - * Helper method to make an HTTP request and return a File object. - * @param url String - * @param auth Authentication - * @param f File that will be written to - * @return File - * @throws HelloSignException thrown if there is a problem with the - * HTTP request or writing to the file - */ - private static File getFile(String url, Authentication auth, File f) - throws HelloSignException { - InputStream in = null; - OutputStream out = null; - try { - HttpURLConnection connection = get(url, null, auth); - int httpCode = connection.getResponseCode(); - if (httpCode == HttpURLConnection.HTTP_OK) { - Files.copy(connection.getInputStream(), f.toPath(), StandardCopyOption.REPLACE_EXISTING); - return f; - } - - // We know we encountered an error here, so let's parse - // the response as an API error message - in = connection.getErrorStream(); - String responseStr = convertStreamToString(in); - try { - JSONObject json = new JSONObject(responseStr); - validate(json, httpCode); - } catch (JSONException e) { - throw new HelloSignException( - "Error encountered attempting to parse API error response: " - + e.getMessage()); - } - - // If we were not able to throw an exception by now, - // throw one, because the user should not be here. - throw new HelloSignException( - "Unable to process response from HelloSign. Please contact support."); - - } catch (HelloSignException ex) { - // Rethrow any exceptions we're throwing ourselves - throw ex; - } catch (Exception e) { - // Wrap any exceptions we receive - throw new HelloSignException(e); - } finally { - // Cleanup - if (in != null) { - try { in.close(); } catch (Exception e) {} - } - if (out != null) { - try { out.close(); } catch (Exception e) {} - } - } - } - } diff --git a/src/test/java/com/hellosign/sdk/resource/EventTest.java b/src/main/java/com/hellosign/sdk/http/HttpOptionsRequest.java similarity index 55% rename from src/test/java/com/hellosign/sdk/resource/EventTest.java rename to src/main/java/com/hellosign/sdk/http/HttpOptionsRequest.java index 0b05c897..3ae05570 100644 --- a/src/test/java/com/hellosign/sdk/resource/EventTest.java +++ b/src/main/java/com/hellosign/sdk/http/HttpOptionsRequest.java @@ -1,9 +1,9 @@ -package com.hellosign.sdk.resource; +package com.hellosign.sdk.http; /** * The MIT License (MIT) * - * Copyright (C) 2015 hellosign.com + * Copyright (C) 2017 hellosign.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -24,30 +24,25 @@ * SOFTWARE. */ -import static org.junit.Assert.*; +import java.net.HttpURLConnection; -import org.junit.Test; - -import com.hellosign.sdk.AbstractHelloSignTest; import com.hellosign.sdk.HelloSignException; -/** - * Exercises the Event object. - * - * @author "Chris Paul (chris@hellosign.com)" - */ -public class EventTest extends AbstractHelloSignTest { - - @Test - public void testEventHashing() throws HelloSignException { - Event event = new Event(getExpectedJSONResponse()); - assertNotNull(event); - assertFalse(event.isValid(null)); - assertFalse(event.isValid("")); +public class HttpOptionsRequest extends AbstractHttpRequest { - // This works for the JSON response in EventTest/expectedResponse.txt - // Ensures that if the hash checking changes, we are notified in the API - assertTrue(event.isValid("4e94adf59d17c417ce0d1d2cb34b953ba7a1eebe1ecbe31bb1c586af92af1e1d")); + public HttpOptionsRequest(String url) { + this.url = url; } + @Override + protected HttpURLConnection getConnection() throws HelloSignException { + HttpURLConnection connection = null; + try { + connection = getProxiedConnection(url); + connection.setRequestMethod("OPTIONS"); + } catch (Exception ex) { + throw new HelloSignException(ex); + } + return connection; + } } diff --git a/src/main/java/com/hellosign/sdk/http/HttpPostRequest.java b/src/main/java/com/hellosign/sdk/http/HttpPostRequest.java index 59689678..46f2310e 100644 --- a/src/main/java/com/hellosign/sdk/http/HttpPostRequest.java +++ b/src/main/java/com/hellosign/sdk/http/HttpPostRequest.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -27,7 +27,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; @@ -39,7 +38,6 @@ import java.util.Iterator; import java.util.Map; -import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,48 +64,49 @@ public class HttpPostRequest extends AbstractHttpRequest { /** * Constructor + * * @param url String - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpPostRequest(String url) - throws HelloSignException { + public HttpPostRequest(String url) throws HelloSignException { this(url, null, null); } /** * Constructor + * * @param url String * @param auth Authentication - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpPostRequest(String url, Authentication auth) - throws HelloSignException { + public HttpPostRequest(String url, Authentication auth) throws HelloSignException { this(url, null, auth); } /** * Constructor + * * @param url String * @param fields Map - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpPostRequest(String url, Map fields) - throws HelloSignException { + public HttpPostRequest(String url, Map fields) throws HelloSignException { this(url, fields, null); } /** * Constructor + * * @param url String * @param fields Map * @param auth Authentication - * @throws HelloSignException thrown if there is a problem making - * the HTTP request or processing the response + * @throws HelloSignException thrown if there is a problem making the HTTP + * request or processing the response */ - public HttpPostRequest(String url, Map fields, Authentication auth) + public HttpPostRequest(String url, Map fields, Authentication auth) throws HelloSignException { if (url == null || "".equals(url)) { throw new HelloSignException("URL cannot be null or empty"); @@ -124,67 +123,15 @@ public HttpPostRequest(String url, Map fields, Authenticat } /** - * Performs a POST request to the given URL, using the authentication - * details and POST fields provided. - * @return JSONObject - * @throws HelloSignException thrown if there is a problem making the HTTP - * request or processing the result - */ - public JSONObject getJsonResponse() throws HelloSignException { - HttpURLConnection connection = post(); - JSONObject json = null; - try { - int httpCode = connection.getResponseCode(); - InputStream response = null; - if (httpCode >= 200 && httpCode < 300) { - logger.debug("OK!"); - response = connection.getInputStream(); - } else { - logger.error("Error! HTTP Code = " + httpCode); - response = connection.getErrorStream(); - } - String responseStr = ""; - if (response == null) { - logger.error("Unable to parse JSON from empty response!"); - } else { - responseStr = convertStreamToString(response); - logger.debug("String Response: " + responseStr); - json = new JSONObject(responseStr); - validate(json, httpCode); - logger.debug("JSON Response: " + json.toString(2)); - } - } catch (HelloSignException e) { - throw e; - } catch (Exception e) { - throw new HelloSignException(e); - } - return json; - } - - /** - * Performs a field-less POST request to the provided URL using basic auth and - * returns the HTTP code. - * @return int HTTP status code - * @throws HelloSignException thrown if there is a problem making the HTTP - * request or processing the result - */ - public int getHttpResponseCode() throws HelloSignException { - HttpURLConnection connection = post(); - try { - return connection.getResponseCode(); - } catch (Exception ex) { - throw new HelloSignException(ex.getMessage()); - } - } - - /** - * Helper method to make an HTTP POST request. Intelligently detects - * whether Files have been attached and sends as an multipart form request. + * Helper method to make an HTTP POST request. Intelligently detects whether + * Files have been attached and sends as an multipart form request. + * * @return HttpUrlConnection * @throws HelloSignException thrown if there is a problem making the HTTP - * request or processing the result + * request or processing the result */ - private HttpURLConnection post() throws HelloSignException { + @Override + protected HttpURLConnection getConnection() throws HelloSignException { if (fields != null) { for (String key : fields.keySet()) { if (fields.get(key) instanceof File) { @@ -197,15 +144,16 @@ private HttpURLConnection post() throws HelloSignException { /** * Helper method to make an HTTP POST request. + * * @return HttpURLConnection * @throws HelloSignException thrown if there is a problem making the HTTP - * request or processing the result + * request or processing the result */ private HttpURLConnection postQuery() throws HelloSignException { logger.debug(this.method + ": " + url); HttpURLConnection connection; try { - connection = getConnection(url); + connection = getProxiedConnection(url); if (!this.method.equals("POST")) { connection.setRequestMethod(method); } @@ -244,9 +192,12 @@ private HttpURLConnection postQuery() throws HelloSignException { try { OutputStream output = connection.getOutputStream(); try { - output.write(sb.toString().getBytes(DEFAULT_ENCODING)); + output.write(sb.toString().getBytes(DEFAULT_ENCODING)); } finally { - try { output.close(); } catch (IOException logOrIgnore) {} + try { + output.close(); + } catch (IOException logOrIgnore) { + } } } catch (Exception ex) { ex.printStackTrace(); @@ -257,9 +208,10 @@ private HttpURLConnection postQuery() throws HelloSignException { /** * Helper method to make an HTTP POST request with a File. + * * @return HttpURLConnection * @throws HelloSignException thrown if there is a problem making the HTTP - * request or processing the result + * request or processing the result */ private HttpURLConnection postWithFile() throws HelloSignException { try { @@ -268,7 +220,7 @@ private HttpURLConnection postWithFile() throws HelloSignException { for (String key : fields.keySet()) { Serializable val = fields.get(key); if (val instanceof File) { - addFilePart(key, (File) val); + addFilePart(key, (File) val); } else { addFormField(key, val.toString()); } @@ -281,44 +233,35 @@ private HttpURLConnection postWithFile() throws HelloSignException { } } - private void openMultipartPostConnection() - throws IOException { - httpConn = getConnection(this.url); + private void openMultipartPostConnection() throws IOException { + httpConn = getProxiedConnection(this.url); httpConn.setUseCaches(false); httpConn.setDoOutput(true); // indicates POST method httpConn.setDoInput(true); - httpConn.setRequestProperty("Content-Type", - "multipart/form-data; boundary=" + boundary); + httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); httpConn.setRequestProperty("User-Agent", USER_AGENT); if (auth != null) { auth.authenticate(httpConn, this.url); } outputStream = httpConn.getOutputStream(); - writer = new PrintWriter(new OutputStreamWriter(outputStream, DEFAULT_ENCODING), - true); + writer = new PrintWriter(new OutputStreamWriter(outputStream, DEFAULT_ENCODING), true); } private void addFormField(String name, String value) { write("--" + boundary).write(LINE_FEED); - write("Content-Disposition: form-data; name=\"" + name + "\"") - .write(LINE_FEED); - write("Content-Type: text/plain; charset=" + DEFAULT_ENCODING) - .write(LINE_FEED); + write("Content-Disposition: form-data; name=\"" + name + "\"").write(LINE_FEED); + write("Content-Type: text/plain; charset=" + DEFAULT_ENCODING).write(LINE_FEED); write(LINE_FEED); write(value).append(LINE_FEED); writer.flush(); } - private void addFilePart(String fieldName, File uploadFile) - throws IOException { + private void addFilePart(String fieldName, File uploadFile) throws IOException { String fileName = uploadFile.getName(); write("--" + boundary).write(LINE_FEED); - write("Content-Disposition: form-data; name=\"" + fieldName - + "\"; filename=\"" + fileName + "\"") - .write(LINE_FEED); - write("Content-Type: " - + URLConnection.guessContentTypeFromName(fileName)) + write("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"") .write(LINE_FEED); + write("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).write(LINE_FEED); write("Content-Transfer-Encoding: binary").write(LINE_FEED); write(LINE_FEED); writer.flush(); diff --git a/src/main/java/com/hellosign/sdk/http/HttpPutRequest.java b/src/main/java/com/hellosign/sdk/http/HttpPutRequest.java index 9f2217f4..966847f9 100644 --- a/src/main/java/com/hellosign/sdk/http/HttpPutRequest.java +++ b/src/main/java/com/hellosign/sdk/http/HttpPutRequest.java @@ -1,5 +1,29 @@ package com.hellosign.sdk.http; +/** + * The MIT License (MIT) + * + * Copyright (C) 2017 hellosign.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + import java.io.Serializable; import java.util.Map; diff --git a/src/main/java/com/hellosign/sdk/resource/AbstractRequest.java b/src/main/java/com/hellosign/sdk/resource/AbstractRequest.java index 9b8935c3..3fa2617e 100644 --- a/src/main/java/com/hellosign/sdk/resource/AbstractRequest.java +++ b/src/main/java/com/hellosign/sdk/resource/AbstractRequest.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -39,9 +39,8 @@ import com.hellosign.sdk.resource.support.Metadata; /** - * Requests to HelloSign will have common fields such as a - * request title, subject, and message. This class centralizes - * those fields. + * Requests to HelloSign will have common fields such as a request title, + * subject, and message. This class centralizes those fields. * * @author "Chris Paul (chris@hellosign.com)" */ @@ -83,8 +82,7 @@ public AbstractRequest() { metadata = new Metadata(); } - public AbstractRequest(JSONObject json, String optionalKey) - throws HelloSignException { + public AbstractRequest(JSONObject json, String optionalKey) throws HelloSignException { super(json, optionalKey); metadata = new Metadata(dataObj); } @@ -135,6 +133,7 @@ public boolean hasTitle() { public String getSubject() { return getString(REQUEST_SUBJECT); } + public void setSubject(String subject) { set(REQUEST_SUBJECT, subject); } @@ -149,7 +148,7 @@ public String getMessage() { public void setMessage(String message) { set(REQUEST_MESSAGE, message); - } + } public boolean hasMessage() { return has(REQUEST_MESSAGE); @@ -181,62 +180,74 @@ public boolean hasRedirectUrl() { public boolean hasUseTextTags() { return has(REQUEST_USE_TEXT_TAGS); } + public Boolean isUsingTextTags() { return getBoolean(REQUEST_USE_TEXT_TAGS); } + public void setUseTextTags(boolean useTextTags) { set(REQUEST_USE_TEXT_TAGS, useTextTags); } + public boolean hasHideTextTags() { return has(REQUEST_HIDE_TEXT_TAGS); } + public Boolean isHidingTextTags() { return getBoolean(REQUEST_HIDE_TEXT_TAGS); } + public void setHideTextTags(boolean hideTextTags) { set(REQUEST_HIDE_TEXT_TAGS, hideTextTags); } + public boolean hasUsePreexistingFields() { return has(REQUEST_USE_PREEXISTING_FIELDS); } + public Boolean isUsingPreexistingFields() { return getBoolean(REQUEST_USE_PREEXISTING_FIELDS); } + public void setUsePreexistingFields(boolean usePreexistingFields) { set(REQUEST_USE_PREEXISTING_FIELDS, usePreexistingFields); } + public Metadata getMetadata() { return metadata; } + public void addMetadata(String key, String value) { metadata.set(key, value); } + public String getMetadata(String key) { return metadata.get(key); } /** - * Adds the file to the request. + * Adds the file to the request. + * * @param file File - * @throws HelloSignException thrown if there is a problem attaching - * the File to this request. + * @throws HelloSignException thrown if there is a problem attaching the + * File to this request. */ public void addFile(File file) throws HelloSignException { addFile(file, null); } /** - * Adds the file to the request in the given order. + * Adds the file to the request in the given order. * - * The order should be a 0-based index into the file list. - * Therefore, the first item of the file list is 0, and so forth. + * The order should be a 0-based index into the file list. Therefore, the + * first item of the file list is 0, and so forth. * * If order is null, the document is appended to the end of the file list. * * @param file File * @param order Integer or null - * @throws HelloSignException thrown if there is a problem attaching - * the File to this request. + * @throws HelloSignException thrown if there is a problem attaching the + * File to this request. */ public void addFile(File file, Integer order) throws HelloSignException { Document doc = new Document(); @@ -250,6 +261,7 @@ public void addFile(File file, Integer order) throws HelloSignException { /** * Adds a Document to the signature request. + * * @param doc Document * @throws HelloSignException thrown if null is provided */ @@ -261,11 +273,12 @@ public void addDocument(Document doc) throws HelloSignException { } /** - * Adds a Document to the signature request at the specific order. + * Adds a Document to the signature request at the specific order. + * * @param doc Document * @param order int - * @throws HelloSignException thrown if null is provided or there - * is a problem attaching the Document. + * @throws HelloSignException thrown if null is provided or there is a + * problem attaching the Document. */ public void addDocument(Document doc, int order) throws HelloSignException { if (doc == null) { @@ -279,9 +292,10 @@ public void addDocument(Document doc, int order) throws HelloSignException { } /** - * Returns a reference to the list of documents for this request. - * Modifying this list will modify the list that will be sent with the - * request. Useful for more fine-grained modification. + * Returns a reference to the list of documents for this request. Modifying + * this list will modify the list that will be sent with the request. Useful + * for more fine-grained modification. + * * @return List */ public List getDocuments() { @@ -290,6 +304,7 @@ public List getDocuments() { /** * Overwrites this requests document list with the provided document list. + * * @param docs List */ public void setDocuments(List docs) { @@ -305,6 +320,7 @@ public void clearDocuments() { /** * Determines whether the order of the signers list is to be enforced. + * * @param b true if the order matters, false otherwise */ public void setOrderMatters(boolean b) { @@ -313,6 +329,7 @@ public void setOrderMatters(boolean b) { /** * A flag that determines whether order of the signers list is enforced. + * * @return true if the order matters, false otherwise */ public boolean getOrderMatters() { @@ -321,6 +338,7 @@ public boolean getOrderMatters() { /** * Add a file_url to this request. + * * @param url String */ public void addFileUrl(String url) { @@ -329,6 +347,7 @@ public void addFileUrl(String url) { /** * Return the current file_url list. + * * @return List */ public List getFileUrls() { @@ -337,6 +356,7 @@ public List getFileUrls() { /** * Overwrite the current file_url list. + * * @param fileUrls List */ public void setFileUrls(List fileUrls) { @@ -344,10 +364,10 @@ public void setFileUrls(List fileUrls) { } /** - * Set the UX version for this request. This determines the version - * of the signer page displayed to signer(s). The default is - * UX_VERSION_1 (non-responsive). Use UX_VERSION_2 for the responsive - * signer page. + * Set the UX version for this request. This determines the version of the + * signer page displayed to signer(s). The default is UX_VERSION_1 + * (non-responsive). Use UX_VERSION_2 for the responsive signer page. + * * @param uxVersion int */ public void setUxVersion(int uxVersion) { @@ -356,6 +376,7 @@ public void setUxVersion(int uxVersion) { /** * Return the UX version for this request. + * * @return int UX version (UX_VERSION_1 or UX_VERSION_2) */ public int getUxVersion() { @@ -364,6 +385,7 @@ public int getUxVersion() { /** * Associates this request with an API app. + * * @param clientId String client ID of the API app. * @throws HelloSignException thrown if clientId is null */ @@ -375,8 +397,9 @@ public void setClientId(String clientId) throws HelloSignException { } /** - * The API app client ID that has been associated with - * this signature request. + * The API app client ID that has been associated with this signature + * request. + * * @return String client ID */ public String getClientId() { @@ -385,16 +408,18 @@ public String getClientId() { /** * Designate this request as declinable by signers. - * @param isDeclinable true if declinable, false otherwise - * (null if the parameter should be left off) + * + * @param isDeclinable true if declinable, false otherwise (null if the + * parameter should be left off) */ public void setIsDeclinable(Boolean isDeclinable) { this.isDeclinable = isDeclinable; } /** - * Retrieve the flag that designates whether this - * request is declinable by signers. + * Retrieve the flag that designates whether this request is declinable by + * signers. + * * @return Boolean or null if the flag has not been set */ public Boolean getIsDeclinable() { diff --git a/src/main/java/com/hellosign/sdk/resource/AbstractResource.java b/src/main/java/com/hellosign/sdk/resource/AbstractResource.java index 9fc17f06..d289f4ca 100644 --- a/src/main/java/com/hellosign/sdk/resource/AbstractResource.java +++ b/src/main/java/com/hellosign/sdk/resource/AbstractResource.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -51,8 +51,7 @@ protected AbstractResource() { dataObj = new JSONObject(); } - protected AbstractResource(JSONObject json, String optionalKey) - throws HelloSignException { + protected AbstractResource(JSONObject json, String optionalKey) throws HelloSignException { try { dataObj = json; if (json.has(optionalKey) && !json.isNull(optionalKey)) { @@ -61,7 +60,8 @@ protected AbstractResource(JSONObject json, String optionalKey) dataObj = (JSONObject) obj; } else if (obj instanceof String) { // This is to handle the case where we're returning a - // stringified JSON object (and should handle strings OK too. + // stringified JSON object (and should handle strings OK + // too. dataObj = new JSONObject((String) obj); } else { throw new HelloSignException("Cannot convert response to JSONObject: " + obj); @@ -183,15 +183,15 @@ protected List getList(Class clazz, String key, Serializable filterVal if (array.length() == 0) { return returnList; } - Constructor constructor = - getConstructor(clazz, array.get(0).getClass()); + Constructor constructor = getConstructor(clazz, array.get(0).getClass()); if (constructor == null) { return returnList; } for (int i = 0; i < array.length(); i++) { Object obj = array.get(i); - // Suppress the warning for the cast, since we checked in getConstructor() - @SuppressWarnings("unchecked") + // Suppress the warning for the cast, since we checked in + // getConstructor() + @SuppressWarnings("unchecked") T newItem = (T) constructor.newInstance(obj); if (newItem != null) { if (filterColumnName == null && filterValue == null) { @@ -208,9 +208,7 @@ protected List getList(Class clazz, String key, Serializable filterVal } } } - } else if (filterValue != null && - filterValue instanceof String && - newItem instanceof String) { + } else if (filterValue != null && filterValue instanceof String && newItem instanceof String) { // If we have a filter value, but no column name, // test for String equality if (filterValue.equals(newItem)) { @@ -252,6 +250,7 @@ protected void addToList(String key, AbstractResource listItem) throws HelloSign /** * Returns the first constructor that has exactly one parameter of the * provided paramClass type. + * * @param clazz Class whose constructors we are checking * @param paramClass Class Parameter class that the constructor should take * @return Constructor diff --git a/src/main/java/com/hellosign/sdk/resource/AbstractResourceList.java b/src/main/java/com/hellosign/sdk/resource/AbstractResourceList.java index e3ec7a79..9ec70f5f 100644 --- a/src/main/java/com/hellosign/sdk/resource/AbstractResourceList.java +++ b/src/main/java/com/hellosign/sdk/resource/AbstractResourceList.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -31,11 +31,15 @@ import org.json.JSONException; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hellosign.sdk.HelloSignException; public abstract class AbstractResourceList extends AbstractResource implements Iterable { + private static final Logger logger = LoggerFactory.getLogger(AbstractResourceList.class); + public static final String LIST_INFO = "list_info"; public static final String PAGE = "page"; public static final String NUM_PAGES = "num_pages"; @@ -56,69 +60,61 @@ protected AbstractResourceList(JSONObject json, String listKey) throws HelloSign } public Integer getPage() { - try { - return listInfo.getInt(PAGE); - } catch (JSONException e) { - e.printStackTrace(); - return null; - } + return getListData(PAGE); } + public Integer getNumPages() { - try { - return listInfo.getInt(NUM_PAGES); - } catch (JSONException e) { - e.printStackTrace(); - return null; - } + return getListData(NUM_PAGES); } + public Integer getNumResults() { - try { - return listInfo.getInt(NUM_RESULTS); - } catch (JSONException e) { - e.printStackTrace(); - return null; - } + return getListData(NUM_RESULTS); } + public Integer getPageSize() { + return getListData(PAGE_SIZE); + } + + private Integer getListData(String key) { + Integer val = null; try { - return listInfo.getInt(PAGE_SIZE); + val = listInfo.getInt(key); } catch (JSONException e) { - e.printStackTrace(); - return null; + logger.error("Unable to retrieve list data for key " + key + ": " + e.getMessage(), e); } + return val; } + public Iterator iterator() { - List list = null; + Iterator iter = null; try { - list = getCurrentPageList(); + iter = getCurrentPageList().iterator(); } catch (HelloSignException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + logger.error(e.getMessage(), e); } - return list.iterator(); + return iter; } /** * Returns the current page of results for this list object. + * * @return List * @throws HelloSignException thrown if the list cannot be generated */ - public List getCurrentPageList() - throws HelloSignException { + public List getCurrentPageList() throws HelloSignException { return filterCurrentPageBy(null, null); } /** - * Filters the current page of results by the given column and value. + * Filters the current page of results by the given column and value. + * * @param columnName String column name to filter by * @param filterValue Serializable matching value * @return List results * @throws HelloSignException thrown if the column name is invalid */ - public List filterCurrentPageBy(String columnName, Serializable filterValue) - throws HelloSignException { - ParameterizedType genericSuperclass = (ParameterizedType) getClass() - .getGenericSuperclass(); + public List filterCurrentPageBy(String columnName, Serializable filterValue) throws HelloSignException { + ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); @SuppressWarnings("unchecked") Class clazz = (Class) genericSuperclass.getActualTypeArguments()[0]; return getList(clazz, listKey, filterValue, columnName); diff --git a/src/main/java/com/hellosign/sdk/resource/Account.java b/src/main/java/com/hellosign/sdk/resource/Account.java index 49cea779..8338cc25 100644 --- a/src/main/java/com/hellosign/sdk/resource/Account.java +++ b/src/main/java/com/hellosign/sdk/resource/Account.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -49,6 +49,7 @@ public class Account extends AbstractResource { public static final String ACCOUNT_PASSWORD = "password"; private Quotas quotas; + private OauthData oauthData; public Account() { super(); @@ -121,11 +122,15 @@ public boolean hasRoleCode() { return has(ACCOUNT_ROLE_CODE); } + public Quotas getQuotas() { + return quotas; + } + public OauthData getOauthData() throws HelloSignException { - return new OauthData((JSONObject) get(OAUTH_DATA)); + return oauthData; } - public Quotas getQuotas() { - return quotas; + public void setOauthData(OauthData data) { + oauthData = data; } } diff --git a/src/main/java/com/hellosign/sdk/resource/ApiApp.java b/src/main/java/com/hellosign/sdk/resource/ApiApp.java index 71d2a0f4..a53ec791 100644 --- a/src/main/java/com/hellosign/sdk/resource/ApiApp.java +++ b/src/main/java/com/hellosign/sdk/resource/ApiApp.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -55,7 +55,7 @@ public class ApiApp extends AbstractResource { public static final String APIAPP_NAME = "name"; public static final String APIAPP_OWNER_ACCOUNT = "owner_account"; public static final String APIAPP_CUSTOM_LOGO = "custom_logo_file"; - + private ApiAppOauth oauth = null; private Account owner_account = null; private File custom_logo = null; @@ -69,11 +69,12 @@ public ApiApp() { } /** - * Constructor that instantiates an ApiApp object based - * on the JSON response from the HelloSign API. + * Constructor that instantiates an ApiApp object based on the JSON response + * from the HelloSign API. + * * @param json JSONObject - * @throws HelloSignException thrown if there is a problem - * parsing the JSONObject + * @throws HelloSignException thrown if there is a problem parsing the + * JSONObject */ public ApiApp(JSONObject json) throws HelloSignException { super(json, APIAPP_KEY); @@ -81,7 +82,8 @@ public ApiApp(JSONObject json) throws HelloSignException { if (dataObj.has(ApiAppOauth.APIAPP_OAUTH_KEY) && !dataObj.isNull(ApiAppOauth.APIAPP_OAUTH_KEY)) { oauth = new ApiAppOauth(dataObj); } - if (dataObj.has(WhiteLabelingOptions.WHITE_LABLELING_OPTIONS_KEY) && !dataObj.isNull(WhiteLabelingOptions.WHITE_LABLELING_OPTIONS_KEY)) { + if (dataObj.has(WhiteLabelingOptions.WHITE_LABLELING_OPTIONS_KEY) + && !dataObj.isNull(WhiteLabelingOptions.WHITE_LABLELING_OPTIONS_KEY)) { white_labeling_options = new WhiteLabelingOptions(dataObj); try { // Re-save the JSON Object back to the parent object, since @@ -95,6 +97,7 @@ public ApiApp(JSONObject json) throws HelloSignException { /** * The app's callback URL (for events). + * * @return String callback URL or null */ public String getCallbackUrl() { @@ -103,6 +106,7 @@ public String getCallbackUrl() { /** * True if the callback URL is non-null. + * * @return Boolean */ public Boolean hasCallbackUrl() { @@ -111,6 +115,7 @@ public Boolean hasCallbackUrl() { /** * Set the callback URL for this API app's events. + * * @param url String */ public void setCallbackUrl(String url) { @@ -119,6 +124,7 @@ public void setCallbackUrl(String url) { /** * The app's client ID. + * * @return String client ID */ public String getClientId() { @@ -127,6 +133,7 @@ public String getClientId() { /** * Returns true if this app has a client ID. + * * @return boolean */ public boolean hasClientId() { @@ -135,6 +142,7 @@ public boolean hasClientId() { /** * The time that the app was created. + * * @return Date */ public Date getCreatedAt() { @@ -143,6 +151,7 @@ public Date getCreatedAt() { /** * The domain name associated with the app. + * * @return String domain name */ public String getDomain() { @@ -151,6 +160,7 @@ public String getDomain() { /** * True if the domain has been set. + * * @return Boolean */ public Boolean hasDomain() { @@ -159,6 +169,7 @@ public Boolean hasDomain() { /** * Set this API app's domain. + * * @param domain String */ public void setDomain(String domain) { @@ -167,6 +178,7 @@ public void setDomain(String domain) { /** * Boolean to indicate if the app has been approved. + * * @return Boolean */ public Boolean isApproved() { @@ -175,6 +187,7 @@ public Boolean isApproved() { /** * The name of the app. + * * @return String name */ public String getName() { @@ -183,6 +196,7 @@ public String getName() { /** * True if the name is set for this API App. + * * @return Boolean */ public Boolean hasName() { @@ -191,6 +205,7 @@ public Boolean hasName() { /** * Set this API app's name. + * * @param name String */ public void setName(String name) { @@ -199,6 +214,7 @@ public void setName(String name) { /** * An object describing the app's OAuth properties. + * * @return ApiAppOauth */ public ApiAppOauth getOauthInfo() { @@ -214,6 +230,7 @@ public void setOAuthCallbackUrl(String url) { /** * Set this API app's OAuth scopes. + * * @param scopes List of ApiAppOauthScopeType */ public void setScopes(Set scopes) { @@ -224,8 +241,9 @@ public void setScopes(Set scopes) { } /** - * Add a scope to this API App's OAuth scope list. - * Duplicates will be ignored. + * Add a scope to this API App's OAuth scope list. Duplicates will be + * ignored. + * * @param scope ApiAppOauthScopeType */ public void addScope(ApiAppOauthScopeType scope) { @@ -247,6 +265,7 @@ public void clearScopes() { /** * Remove the specified OAuth scope from this API App. + * * @param scope ApiAppOauthScopeType */ public void removeScope(ApiAppOauthScopeType scope) { @@ -259,9 +278,8 @@ public void removeScope(ApiAppOauthScopeType scope) { /** * An object describing the app's owner. * - * NOTE: This Account object will only have the owner's - * account ID and email address. All other values will - * be null. + * NOTE: This Account object will only have the owner's account ID and email + * address. All other values will be null. * * @return Account */ @@ -271,6 +289,7 @@ public Account getOwnerAccount() { /** * Add a custom logo image to this API app. + * * @param f File */ public void setCustomLogo(File f) { @@ -280,9 +299,10 @@ public void setCustomLogo(File f) { /** * Internal method used to retrieve the necessary POST fields to submit the * API app to HelloSign. + * * @return Map * @throws HelloSignException thrown if there is a problem serializing the - * POST fields. + * POST fields. */ public Map getPostFields() throws HelloSignException { Map fields = new HashMap(); @@ -325,6 +345,7 @@ public Map getPostFields() throws HelloSignException { /** * Overrides all white labeling options for this API app. + * * @param options WhiteLabelingOptions */ public void setWhiteLabelingOptions(WhiteLabelingOptions options) { @@ -333,15 +354,16 @@ public void setWhiteLabelingOptions(WhiteLabelingOptions options) { /** * Returns the current white labeling options for this API app. + * * @return WhiteLabelingOptions */ public WhiteLabelingOptions getWhiteLabelingOptions() { return white_labeling_options; } - /** * Get the signer page background color. + * * @return String hex color code */ public String getPageBackgroundColor() { @@ -353,8 +375,10 @@ public String getPageBackgroundColor() { /** * Set the signer page background color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setPageBackgroundColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -365,6 +389,7 @@ public void setPageBackgroundColor(String color) throws HelloSignException { /** * Get the signer page header background color. + * * @return String hex color code */ public String getHeaderBackgroundColor() { @@ -376,8 +401,10 @@ public String getHeaderBackgroundColor() { /** * Set the signer page header background color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setHeaderBackgroundColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -388,6 +415,7 @@ public void setHeaderBackgroundColor(String color) throws HelloSignException { /** * Get the signer page text 1 color. + * * @return String hex color code */ public String getTextColor1() { @@ -399,8 +427,10 @@ public String getTextColor1() { /** * Set the signer page text 1 color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setTextColor1(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -411,6 +441,7 @@ public void setTextColor1(String color) throws HelloSignException { /** * Get the signer page text 2 color. + * * @return String hex color code */ public String getTextColor2() { @@ -422,8 +453,10 @@ public String getTextColor2() { /** * Set the signer page text 2 color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setTextColor2(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -434,6 +467,7 @@ public void setTextColor2(String color) throws HelloSignException { /** * Get the signer page link color. + * * @return String hex color code */ public String getLinkColor() { @@ -445,8 +479,10 @@ public String getLinkColor() { /** * Set the signer page link color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setLinkColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -457,6 +493,7 @@ public void setLinkColor(String color) throws HelloSignException { /** * Get the signer page primary button color. + * * @return String hex color code */ public String getPrimaryButtonColor() { @@ -468,8 +505,10 @@ public String getPrimaryButtonColor() { /** * Set the signer page primary button color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setPrimaryButtonColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -480,6 +519,7 @@ public void setPrimaryButtonColor(String color) throws HelloSignException { /** * Get the signer page primary button text color. + * * @return String hex color code */ public String getPrimaryButtonTextColor() { @@ -491,8 +531,10 @@ public String getPrimaryButtonTextColor() { /** * Set the signer page primary button text color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setPrimaryButtonTextColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -503,6 +545,7 @@ public void setPrimaryButtonTextColor(String color) throws HelloSignException { /** * Get the signer page primary button hover color. + * * @return String hex color code */ public String getPrimaryButtonHoverColor() { @@ -514,8 +557,10 @@ public String getPrimaryButtonHoverColor() { /** * Set the signer page primary button hover color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setPrimaryButtonHoverColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -526,6 +571,7 @@ public void setPrimaryButtonHoverColor(String color) throws HelloSignException { /** * Get the signer page primary button text hover color. + * * @return String hex color code */ public String getPrimaryButtonTextHoverColor() { @@ -537,8 +583,10 @@ public String getPrimaryButtonTextHoverColor() { /** * Set the signer page primary button text hover color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setPrimaryButtonTextHoverColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -549,6 +597,7 @@ public void setPrimaryButtonTextHoverColor(String color) throws HelloSignExcepti /** * Get the signer page secondary button color. + * * @return String hex color code */ public String getSecondaryButtonColor() { @@ -560,8 +609,10 @@ public String getSecondaryButtonColor() { /** * Set the signer page secondary button color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setSecondaryButtonColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -572,6 +623,7 @@ public void setSecondaryButtonColor(String color) throws HelloSignException { /** * Get the signer page secondary button text color. + * * @return String hex color code */ public String getSecondaryButtonTextColor() { @@ -583,8 +635,10 @@ public String getSecondaryButtonTextColor() { /** * Set the signer page secondary button text color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setSecondaryButtonTextColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -595,6 +649,7 @@ public void setSecondaryButtonTextColor(String color) throws HelloSignException /** * Get the signer page secondary button hover color. + * * @return String hex color code */ public String getSecondaryButtonHoverColor() { @@ -606,8 +661,10 @@ public String getSecondaryButtonHoverColor() { /** * Set the signer page secondary button hover color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setSecondaryButtonHoverColor(String color) throws HelloSignException { if (white_labeling_options == null) { @@ -618,6 +675,7 @@ public void setSecondaryButtonHoverColor(String color) throws HelloSignException /** * Get the signer page secondary button text hover color. + * * @return String hex color code */ public String getSecondaryButtonTextHoverColor() { @@ -629,8 +687,10 @@ public String getSecondaryButtonTextHoverColor() { /** * Set the signer page secondary button text hover color. + * * @param color String hex color code - * @throws HelloSignException thrown if the color string is an invalid hex string + * @throws HelloSignException thrown if the color string is an invalid hex + * string */ public void setSecondaryButtonTextHoverColor(String color) throws HelloSignException { if (white_labeling_options == null) { diff --git a/src/main/java/com/hellosign/sdk/resource/EmbeddedRequest.java b/src/main/java/com/hellosign/sdk/resource/EmbeddedRequest.java index d80b54b2..e5f0bc92 100644 --- a/src/main/java/com/hellosign/sdk/resource/EmbeddedRequest.java +++ b/src/main/java/com/hellosign/sdk/resource/EmbeddedRequest.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -30,8 +30,8 @@ import com.hellosign.sdk.HelloSignException; /** - * Represents an Embedded signature request (either standard or templated). - * An embedded request is one that can be signed from either within HelloSign or + * Represents an Embedded signature request (either standard or templated). An + * embedded request is one that can be signed from either within HelloSign or * from within an iFrame on your website. * * @author "Chris Paul (chris@hellosign.com)" @@ -43,16 +43,18 @@ public class EmbeddedRequest extends AbstractRequest { private String clientId; private AbstractRequest request; - // Hide the default constructor -- we want to force the user to specify a + // Hide the default constructor -- we want to force the user to specify a // request object that this Embedded request will wrap around. @SuppressWarnings("unused") - private EmbeddedRequest() {} + private EmbeddedRequest() { + } /** - * Creates an Embedded signature request using the client ID and the - * AbstractRequest object provided. First, instantiate the request object - * (either a SignatureRequest or TemplateSignatureRequest) and then - * create your EmbeddedRequest using that object. + * Creates an Embedded signature request using the client ID and the + * AbstractRequest object provided. First, instantiate the request object + * (either a SignatureRequest or TemplateSignatureRequest) and then create + * your EmbeddedRequest using that object. + * * @param clientId String client ID * @param request AbstractRequest */ @@ -62,8 +64,9 @@ public EmbeddedRequest(String clientId, AbstractRequest request) { } /** - * Set the client ID of the web app you're using to create this embedded signature - * request. See: http://www.hellosign.com/api/embedded + * Set the client ID of the web app you're using to create this embedded + * signature request. See: http://app.hellosign.com/api/embedded + * * @return String client ID */ public String getClientId() { @@ -71,8 +74,9 @@ public String getClientId() { } /** - * Set the client ID of the web app you're using to create this embedded signature - * request. See: http://www.hellosign.com/api/embedded + * Set the client ID of the web app you're using to create this embedded + * signature request. See: http://app.hellosign.com/api/embedded + * * @param clientId String client ID */ public void setClientId(String clientId) { @@ -81,6 +85,7 @@ public void setClientId(String clientId) { /** * Get the AbstractRequest associated with this Embedded signature request. + * * @return AbstractRequest */ public AbstractRequest getRequest() { @@ -89,6 +94,7 @@ public AbstractRequest getRequest() { /** * Set the AbstractRequest associated with this Embedded signature request. + * * @param request AbstractRequest */ public void setRequest(AbstractRequest request) { @@ -101,9 +107,9 @@ public Map getPostFields() throws HelloSignException { return map; } - //========================================================================================= + // ========================================================================================= // Overrides to pass through to request object - //========================================================================================= + // ========================================================================================= @Override public String getTitle() { @@ -178,5 +184,5 @@ public boolean hasRedirectUrl() { @Override public String getId() { return request.getId(); - } + } } diff --git a/src/main/java/com/hellosign/sdk/resource/EmbeddedResponse.java b/src/main/java/com/hellosign/sdk/resource/EmbeddedResponse.java index be8b2e48..4ffbba23 100644 --- a/src/main/java/com/hellosign/sdk/resource/EmbeddedResponse.java +++ b/src/main/java/com/hellosign/sdk/resource/EmbeddedResponse.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/src/main/java/com/hellosign/sdk/resource/Event.java b/src/main/java/com/hellosign/sdk/resource/Event.java index deeca461..06f70404 100644 --- a/src/main/java/com/hellosign/sdk/resource/Event.java +++ b/src/main/java/com/hellosign/sdk/resource/Event.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -39,8 +39,8 @@ import com.hellosign.sdk.resource.support.types.EventType; /** - * This is a utility class for assisting with the development of callback - * services to respond to HelloSign events. + * This is a utility class for assisting with the development of callback + * services to respond to HelloSign events. * * @author "Chris Paul (chris@hellosign.com)" */ @@ -61,11 +61,12 @@ public class Event extends AbstractResource { private AbstractResource resource; /** - * Default constructor. Provide this constructor with the JSONObject - * created from the API response. + * Default constructor. Provide this constructor with the JSONObject created + * from the API response. + * * @param json JSONObject * @throws HelloSignException thrown if there is a problem parsing the - * JSONObject. + * JSONObject. */ public Event(JSONObject json) throws HelloSignException { super(json, EVENT_KEY); @@ -89,9 +90,10 @@ public Event(JSONObject json) throws HelloSignException { /** * Returns the account ID that this event is reporting for. + * * @return String - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSON object. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSON object. */ public String getAccountId() throws HelloSignException { JSONObject metadata = (JSONObject) get(EVENT_METADATA); @@ -106,9 +108,10 @@ public String getAccountId() throws HelloSignException { /** * Returns true if this event has a "reported_for_account_id" field. + * * @return true or false if it does not have an account ID - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSONObject. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSONObject. */ public boolean hasAccountId() throws HelloSignException { return has(REPORTED_FOR_ACCOUNT_ID); @@ -116,9 +119,10 @@ public boolean hasAccountId() throws HelloSignException { /** * Returns the API app ID for which this event is reported. + * * @return String - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSONObject. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSONObject. */ public String getAppId() throws HelloSignException { JSONObject metadata = (JSONObject) get(EVENT_METADATA); @@ -133,9 +137,10 @@ public String getAppId() throws HelloSignException { /** * Returns true if this event has a "reported_for_app_id" field. + * * @return boolean - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSONObject. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSONObject. */ public boolean hasAppId() throws HelloSignException { return has(REPORTED_FOR_APP_ID); @@ -143,19 +148,21 @@ public boolean hasAppId() throws HelloSignException { /** * Returns the event time as a Java Date object. + * * @return Date */ public Date getEventDate() { return getDate(EVENT_TIME); } - + /** * Returns the message if any from the event + * * @return String - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSONObject. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSONObject. */ - public String getEventMessage() throws HelloSignException{ + public String getEventMessage() throws HelloSignException { JSONObject metadata = (JSONObject) get(EVENT_METADATA); String eventMessage = null; try { @@ -167,10 +174,11 @@ public String getEventMessage() throws HelloSignException{ } /** - * Returns the signature ID to which this event is associated. + * Returns the signature ID to which this event is associated. + * * @return String - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSONObject. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSONObject. */ public String getRelatedSignatureId() throws HelloSignException { JSONObject metadata = (JSONObject) get(EVENT_METADATA); @@ -187,7 +195,8 @@ public String getRelatedSignatureId() throws HelloSignException { } /** - * Utility method to determine if this event has a related signature ID. + * Utility method to determine if this event has a related signature ID. + * * @return true if this field exists and is set, false otherwise */ public boolean hasRelatedSignatureId() { @@ -199,11 +208,12 @@ public boolean hasRelatedSignatureId() { } /** - * Returns the associated Signature object with this event, if the - * event is associated with a Signature Request. + * Returns the associated Signature object with this event, if the event is + * associated with a Signature Request. + * * @return Signature - * @throws HelloSignException thrown if there is a problem parsing - * the backing JSONObject. + * @throws HelloSignException thrown if there is a problem parsing the + * backing JSONObject. */ public Signature getRelatedSignature() throws HelloSignException { String id = getRelatedSignatureId(); @@ -221,6 +231,7 @@ public Signature getRelatedSignature() throws HelloSignException { /** * Returns a reference to the SignatureRequest that is attached to the * Event. + * * @return SignatureRequest */ public SignatureRequest getSignatureRequest() { @@ -232,6 +243,7 @@ public SignatureRequest getSignatureRequest() { /** * Returns true if this Event is associated with a Signature Request. + * * @return boolean */ public boolean hasSignatureRequest() { @@ -240,6 +252,7 @@ public boolean hasSignatureRequest() { /** * Returns a reference to the Template that is attached to the Event. + * * @return Template */ public Template getTemplate() { @@ -251,6 +264,7 @@ public Template getTemplate() { /** * Returns true if this event is associated with a Template. + * * @return boolean */ public boolean hasTemplate() { @@ -259,6 +273,7 @@ public boolean hasTemplate() { /** * Returns the EventType for this event. + * * @return EventType enum */ public EventType getType() { @@ -267,6 +282,7 @@ public EventType getType() { /** * Returns the event type String. + * * @return String event type */ public String getTypeString() { @@ -275,6 +291,7 @@ public String getTypeString() { /** * Returns the unique hash string identifying this event. + * * @return String hash */ public String getHash() { @@ -282,12 +299,13 @@ public String getHash() { } /** - * Returns true if the event hash matches the computed hash using the provided - * API key. + * Returns true if the event hash matches the computed hash using the + * provided API key. + * * @param apiKey String api key. * @return true if the hashes match, false otherwise - * @throws HelloSignException thrown if there is a problem parsing - * the API key. + * @throws HelloSignException thrown if there is a problem parsing the API + * key. */ public boolean isValid(String apiKey) throws HelloSignException { if (apiKey == null || apiKey == "") { @@ -306,7 +324,7 @@ public boolean isValid(String apiKey) throws HelloSignException { } catch (IllegalArgumentException e) { throw new HelloSignException("Invalid API Key (" + e.getMessage() + "): " + apiKey); } catch (NoSuchAlgorithmException e) { - throw new HelloSignException("Unable to process API key", e); + throw new HelloSignException("Unable to process API key", e); } } diff --git a/src/main/java/com/hellosign/sdk/resource/SignatureRequest.java b/src/main/java/com/hellosign/sdk/resource/SignatureRequest.java index 4d5f0d78..7143634c 100644 --- a/src/main/java/com/hellosign/sdk/resource/SignatureRequest.java +++ b/src/main/java/com/hellosign/sdk/resource/SignatureRequest.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -33,6 +33,7 @@ import org.json.JSONObject; import com.hellosign.sdk.HelloSignException; +import com.hellosign.sdk.resource.support.CustomField; import com.hellosign.sdk.resource.support.Document; import com.hellosign.sdk.resource.support.FormField; import com.hellosign.sdk.resource.support.ResponseData; @@ -40,8 +41,8 @@ import com.hellosign.sdk.resource.support.Signer; /** - * Represents a HelloSign signature request. This object is used to both - * submit a request and to represent the request object returned from the server. + * Represents a HelloSign signature request. This object is used to both submit + * a request and to represent the request object returned from the server. * * @author "Chris Paul (chris@hellosign.com)" */ @@ -65,6 +66,7 @@ public class SignatureRequest extends AbstractRequest { public static final String SIGREQ_SIGNING_URL = "signing_url"; public static final String SIGREQ_DETAILS_URL = "details_url"; public static final String SIGREQ_IS_DECLINED = "is_declined"; + public static final String SIGREQ_CUSTOM_FIELDS = "custom_fields"; public static final String SIGREQ_FORMAT_ZIP = "zip"; public static final String SIGREQ_FORMAT_PDF = "pdf"; @@ -92,8 +94,9 @@ public String getId() { } /** - * Returns true if this request has an ID. Useful if checking to see if - * this request is for submission or is the result of a call to HelloSign. + * Returns true if this request has an ID. Useful if checking to see if this + * request is for submission or is the result of a call to HelloSign. + * * @return true if the request has an ID, false otherwise */ public boolean hasId() { @@ -102,6 +105,7 @@ public boolean hasId() { /** * Returns the CC email addresses for this request. + * * @return List */ public List getCCs() { @@ -110,6 +114,7 @@ public List getCCs() { /** * Adds a CC'd email address to this request. + * * @param email String email address */ public void addCC(String email) { @@ -118,6 +123,7 @@ public void addCC(String email) { /** * Returns a list of signatures for this request. + * * @return List */ public List getSignatures() { @@ -125,15 +131,15 @@ public List getSignatures() { } /** - * Returns the signature for the given email/name combination, or - * null if not found on this request. + * Returns the signature for the given email/name combination, or null if + * not found on this request. + * * @param email String email address * @param name String name * @return Signature or null if not found * @throws HelloSignException if the email or name are empty */ - public Signature getSignature(String email, String name) - throws HelloSignException { + public Signature getSignature(String email, String name) throws HelloSignException { if (email == null || "".equals(email)) { throw new HelloSignException("Email address cannot be empty"); } @@ -150,8 +156,10 @@ public Signature getSignature(String email, String name) /** * Adds the signer to the list of signers for this request. + * * @param signer Signer - * @throws HelloSignException thrown if there is a problem adding the signer. + * @throws HelloSignException thrown if there is a problem adding the + * signer. */ public void addSigner(Signer signer) throws HelloSignException { signers.add(signer); @@ -159,28 +167,30 @@ public void addSigner(Signer signer) throws HelloSignException { /** * Adds the signer to the list of signers for this request. + * * @param email String * @param name String - * @throws HelloSignException thrown if there is a problem adding the signer. + * @throws HelloSignException thrown if there is a problem adding the + * signer. */ public void addSigner(String email, String name) throws HelloSignException { signers.add(new Signer(email, name)); } /** - * Adds the signer with the given order to the list of signers for - * this request. NOTE: The order refers to the 1-base index, not 0-base. - * This is to reflect the indexing used by the HelloSign API. - * This means that adding an item at order 1 will place it in the 0th - * index of the list (it will be the first item). + * Adds the signer with the given order to the list of signers for this + * request. NOTE: The order refers to the 1-base index, not 0-base. This is + * to reflect the indexing used by the HelloSign API. This means that adding + * an item at order 1 will place it in the 0th index of the list (it will be + * the first item). * * @param email String * @param name String * @param order int - * @throws HelloSignException thrown if there is a problem adding the signer. + * @throws HelloSignException thrown if there is a problem adding the + * signer. */ - public void addSigner(String email, String name, int order) - throws HelloSignException { + public void addSigner(String email, String name, int order) throws HelloSignException { try { signers.add((order - 1), new Signer(email, name)); } catch (Exception ex) { @@ -189,9 +199,10 @@ public void addSigner(String email, String name, int order) } /** - * Returns a reference to the signers list. This can be modified and - * re-added to the request using setSigners(). Useful for more explicit + * Returns a reference to the signers list. This can be modified and + * re-added to the request using setSigners(). Useful for more explicit * modification. + * * @return List */ public List getSigners() { @@ -199,8 +210,9 @@ public List getSigners() { } /** - * Overwrites the current list of signers for this request with the - * given list. + * Overwrites the current list of signers for this request with the given + * list. + * * @param signers List */ public void setSigners(List signers) { @@ -208,10 +220,12 @@ public void setSigners(List signers) { } /** - * Removes the signer from the list. If that user does not exist, - * this will throw a HelloSignException. + * Removes the signer from the list. If that user does not exist, this will + * throw a HelloSignException. + * * @param email String - * @throws HelloSignException thrown if there is a problem removing the signer. + * @throws HelloSignException thrown if there is a problem removing the + * signer. */ public void removeSigner(String email) throws HelloSignException { if (email == null) { @@ -225,10 +239,11 @@ public void removeSigner(String email) throws HelloSignException { } /** - * Utility method that allows you to search for a Signature object - * on this request by email and name. It requires both because neither - * alone is enough to guarantee uniqueness (some requests can have - * multiple signers using the same email address or name). + * Utility method that allows you to search for a Signature object on this + * request by email and name. It requires both because neither alone is + * enough to guarantee uniqueness (some requests can have multiple signers + * using the same email address or name). + * * @param email String * @param name String * @return Signature, if found on this request, or null @@ -239,8 +254,7 @@ public Signature getSignatureBySigner(String email, String name) { return null; } for (Signature s : getSignatures()) { - if (name.equalsIgnoreCase(s.getName()) && - email.equalsIgnoreCase(s.getEmail())) { + if (name.equalsIgnoreCase(s.getName()) && email.equalsIgnoreCase(s.getEmail())) { return s; } } @@ -249,10 +263,11 @@ public Signature getSignatureBySigner(String email, String name) { /** * Internal method used to retrieve the necessary POST fields to submit the - * signature request. + * signature request. + * * @return Map * @throws HelloSignException thrown if there is a problem serializing the - * POST fields. + * POST fields. */ public Map getPostFields() throws HelloSignException { Map fields = super.getPostFields(); @@ -270,21 +285,18 @@ public Map getPostFields() throws HelloSignException { for (int i = 0; i < signerz.size(); i++) { Signer s = signerz.get(i); - // The signers are being ID'd starting at 1, instead of zero. - // This is because the API generates signer IDs for templates starting at 1. + // The signers are being ID'd starting at 1, instead of zero. + // This is because the API generates signer IDs for templates + // starting at 1. // Let's keep this consistent with the API for now. - fields.put(SIGREQ_SIGNERS + - "[" + (i + 1) + "][" + SIGREQ_SIGNER_EMAIL + "]", s.getEmail()); - fields.put(SIGREQ_SIGNERS + - "[" + (i + 1) + "][" + SIGREQ_SIGNER_NAME + "]", s.getNameOrRole()); + fields.put(SIGREQ_SIGNERS + "[" + (i + 1) + "][" + SIGREQ_SIGNER_EMAIL + "]", s.getEmail()); + fields.put(SIGREQ_SIGNERS + "[" + (i + 1) + "][" + SIGREQ_SIGNER_NAME + "]", s.getNameOrRole()); if (getOrderMatters()) { - fields.put(SIGREQ_SIGNERS + - "[" + (i + 1) + "][" + SIGREQ_SIGNER_ORDER + "]", i); + fields.put(SIGREQ_SIGNERS + "[" + (i + 1) + "][" + SIGREQ_SIGNER_ORDER + "]", i); } - if(s.getAccessCode() != null) { - fields.put(SIGREQ_SIGNERS + - "[" + (i + 1) + "][" + SIGREQ_SIGNER_PIN + "]", s.getAccessCode()); + if (s.getAccessCode() != null) { + fields.put(SIGREQ_SIGNERS + "[" + (i + 1) + "][" + SIGREQ_SIGNER_PIN + "]", s.getAccessCode()); } } List ccz = getCCs(); @@ -292,7 +304,8 @@ public Map getPostFields() throws HelloSignException { String cc = ccz.get(i); fields.put(SIGREQ_CCS + "[" + (i + 1) + "]", cc); } - JSONArray reqFormFields = new JSONArray(); // Main array for the request + JSONArray reqFormFields = new JSONArray(); // Main array for the + // request boolean hasFormFields = false; List docs = getDocuments(); for (int i = 0; i < docs.size(); i++) { @@ -313,7 +326,7 @@ public Map getPostFields() throws HelloSignException { fields.put(SIGREQ_FORM_FIELDS, reqFormFields.toString()); } if (isTestMode()) { - fields.put(REQUEST_TEST_MODE, true); + fields.put(REQUEST_TEST_MODE, true); } if (hasRedirectUrl()) { fields.put(REQUEST_REDIRECT_URL, getRedirectUrl()); @@ -325,27 +338,29 @@ public Map getPostFields() throws HelloSignException { fields.put(REQUEST_HIDE_TEXT_TAGS, isHidingTextTags()); } } catch (Exception ex) { - throw new HelloSignException( - "Could not extract form fields from SignatureRequest.", ex); + throw new HelloSignException("Could not extract form fields from SignatureRequest.", ex); } return fields; } /** - * Returns the HelloSign-designated signature status, indicating - * whether all signers have signed the document. - * @return true, if all signers have signed the document, false - * otherwise. + * Returns the HelloSign-designated signature status, indicating whether all + * signers have signed the document. + * + * @return true, if all signers have signed the document, false otherwise. */ public boolean isComplete() { return getBoolean(SIGREQ_IS_COMPLETE); } + public boolean hasError() { return getBoolean(SIGREQ_HAS_ERROR); } + public List getResponseData() { return getList(ResponseData.class, SIGREQ_RESPONSE_DATA); } + /** * @deprecated use getFilesUrl() * @return String URL @@ -353,23 +368,38 @@ public List getResponseData() { public String getFinalCopyUrl() { return getString(SIGREQ_FINAL_COPY_URL); } + /** * Returns the API URL to retrieve the PDF copy of this signature request. + * * @return String URL */ public String getFilesUrl() { return getString(SIGREQ_FILES_URL); } + public String getSigningUrl() { return getString(SIGREQ_SIGNING_URL); } + public String getDetailsUrl() { return getString(SIGREQ_DETAILS_URL); } + public boolean isDeclined() { if (has(SIGREQ_IS_DECLINED)) { return getBoolean(SIGREQ_IS_DECLINED); } return false; } + + /** + * Gets the custom fields associated with this request, set when sending the + * request from a template. + * + * @return List CustomFields + */ + public List getCustomFields() { + return getList(CustomField.class, SIGREQ_CUSTOM_FIELDS); + } } \ No newline at end of file diff --git a/src/main/java/com/hellosign/sdk/resource/Team.java b/src/main/java/com/hellosign/sdk/resource/Team.java index a5808c01..a2728c18 100644 --- a/src/main/java/com/hellosign/sdk/resource/Team.java +++ b/src/main/java/com/hellosign/sdk/resource/Team.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/src/main/java/com/hellosign/sdk/resource/Template.java b/src/main/java/com/hellosign/sdk/resource/Template.java index 1894dc80..2914a650 100644 --- a/src/main/java/com/hellosign/sdk/resource/Template.java +++ b/src/main/java/com/hellosign/sdk/resource/Template.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -56,7 +56,8 @@ public class Template extends AbstractResource { public Template() { super(); - } + } + public Template(JSONObject json) throws HelloSignException { super(json, TEMPLATE_KEY); } @@ -64,27 +65,35 @@ public Template(JSONObject json) throws HelloSignException { public String getId() { return getString(TEMPLATE_ID); } + public boolean hasId() { return has(TEMPLATE_ID); } + public String getTitle() { return getString(TEMPLATE_TITLE); } + public boolean hasTitle() { return has(TEMPLATE_TITLE); } + public String getMessage() { return getString(TEMPLATE_MESSAGE); } + public boolean hasMessage() { return has(TEMPLATE_MESSAGE); } + public List getDocuments() { return getList(Document.class, TEMPLATE_DOCUMENTS); } + /** * Returns a list of SignerRoles. If they are ordered, the list is returned * in order. + * * @return List */ public List getSignerRoles() { @@ -101,18 +110,23 @@ public List getSignerRoles() { } return sortedList; } + public List getCCRoles() { return getList(TemplateRole.class, TEMPLATE_CC_ROLES); } + public List getAccounts() { return getList(Account.class, TEMPLATE_ACCOUNTS); } + public boolean canEdit() { return getBoolean(TEMPLATE_CAN_EDIT); } + public boolean isCreator() { return getBoolean(TEMPLATE_IS_CREATOR); } + public List getCustomFields() { return getList(CustomField.class, TEMPLATE_CUSTOM_FIELDS); } @@ -120,6 +134,7 @@ public List getCustomFields() { /** * Returns true if this template was created on a site other than * hellosign.com. + * * @return boolean */ public boolean isEmbedded() { diff --git a/src/main/java/com/hellosign/sdk/resource/TemplateDraft.java b/src/main/java/com/hellosign/sdk/resource/TemplateDraft.java index 01ffeb96..058b9922 100644 --- a/src/main/java/com/hellosign/sdk/resource/TemplateDraft.java +++ b/src/main/java/com/hellosign/sdk/resource/TemplateDraft.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -69,8 +69,9 @@ public String getId() { } /** - * Returns true if this request has an ID. Useful if checking to see if - * this request is for submission or is the result of a call to HelloSign. + * Returns true if this request has an ID. Useful if checking to see if this + * request is for submission or is the result of a call to HelloSign. + * * @return true if the request has an ID, false otherwise */ public boolean hasId() { @@ -79,6 +80,7 @@ public boolean hasId() { /** * Returns the CC roles for this request. + * * @return List */ public List getCCRoles() { @@ -86,8 +88,9 @@ public List getCCRoles() { } /** - * Adds a named role for a CC recipient that must be specified - * when the template is used. + * Adds a named role for a CC recipient that must be specified when the + * template is used. + * * @param ccRole String */ public void addCCRole(String ccRole) { @@ -96,6 +99,7 @@ public void addCCRole(String ccRole) { /** * Adds the signer role to the template draft. + * * @param signerRole String */ public void addSignerRole(String signerRole) { @@ -104,6 +108,7 @@ public void addSignerRole(String signerRole) { /** * Returns the signer roles specified for this template draft. + * * @return List */ public List getSignerRoles() { @@ -111,19 +116,18 @@ public List getSignerRoles() { } /** - * Adds the signer role with the given order to the list of signers for - * this request. NOTE: The order refers to the 1-base index, not 0-base. - * This is to reflect the indexing used by the HelloSign API. - * This means that adding an item at order 1 will place it in the 0th - * index of the list (it will be the first item). + * Adds the signer role with the given order to the list of signers for this + * request. NOTE: The order refers to the 1-base index, not 0-base. This is + * to reflect the indexing used by the HelloSign API. This means that adding + * an item at order 1 will place it in the 0th index of the list (it will be + * the first item). * * @param role String * @param order int - * @throws HelloSignException thrown if there is a problem adding - * the signer role. + * @throws HelloSignException thrown if there is a problem adding the signer + * role. */ - public void addSignerRole(String role, int order) - throws HelloSignException { + public void addSignerRole(String role, int order) throws HelloSignException { try { signerRoles.add((order - 1), role); } catch (Exception ex) { @@ -133,6 +137,7 @@ public void addSignerRole(String role, int order) /** * Overwrites the current list of signer roles. + * * @param signerRoles List */ public void setSignerRoles(List signerRoles) { @@ -141,10 +146,11 @@ public void setSignerRoles(List signerRoles) { /** * Removes the signer role. + * * @param signerRole String * @return boolean - * @throws HelloSignException thrown if there is a problem removing - * the signer role. + * @throws HelloSignException thrown if there is a problem removing the + * signer role. */ public boolean removeSignerRole(String signerRole) throws HelloSignException { return signerRoles.remove(signerRole); @@ -154,11 +160,13 @@ public boolean removeSignerRole(String signerRole) throws HelloSignException { * Add merge fields to the template draft. These are fields that your app * can pre-populate whenever the *finished* template is used to send a * signature request. - * @param name String name of the merge field that will be displayed to - * the user and used to key off the custom field when populating the value. + * + * @param name String name of the merge field that will be displayed to the + * user and used to key off the custom field when populating the + * value. * @param type FieldType (currently only "text" and "checkbox" are allowed) - * @throws HelloSignException thrown if there is a problem adding the - * merge field. + * @throws HelloSignException thrown if there is a problem adding the merge + * field. */ public void addMergeField(String name, FieldType type) throws HelloSignException { if (!FieldType.checkbox.equals(type) && !FieldType.text.equals(type)) { @@ -169,6 +177,7 @@ public void addMergeField(String name, FieldType type) throws HelloSignException /** * Returns the current map of merge field names to types. + * * @return Map */ public Map getMergeFields() { @@ -184,7 +193,8 @@ public void clearMergeFields() { /** * Utility method to detect whether the "edit_url" parameter is set on this - * template object. + * template object. + * * @return boolean */ public boolean hasEditUrl() { @@ -193,6 +203,7 @@ public boolean hasEditUrl() { /** * Returns the edit URL for creating an embedded template draft. + * * @return String edit URL */ public String getEditUrl() { @@ -200,8 +211,9 @@ public String getEditUrl() { } /** - * Utility method to detect whether the "expires_at" parameter is set on this - * template object. + * Utility method to detect whether the "expires_at" parameter is set on + * this template object. + * * @return boolean */ public boolean hasExpiresAt() { @@ -210,6 +222,7 @@ public boolean hasExpiresAt() { /** * Returns the expiration time for the edit URL of this template. + * * @return String expiration timestamp */ public String getExpiresAt() { @@ -218,9 +231,10 @@ public String getExpiresAt() { /** * Internal method used to retrieve the necessary POST fields. + * * @return Map - * @throws HelloSignException thrown if there is a problem serializing - * the POST fields. + * @throws HelloSignException thrown if there is a problem serializing the + * POST fields. */ public Map getPostFields() throws HelloSignException { Map fields = super.getPostFields(); @@ -238,8 +252,9 @@ public Map getPostFields() throws HelloSignException { for (int i = 0; i < signerRoles.size(); i++) { String s = signerRoles.get(i); - // The signers are being ID'd starting at 1, instead of zero. - // This is because the API generates signer IDs for templates starting at 1. + // The signers are being ID'd starting at 1, instead of zero. + // This is because the API generates signer IDs for templates + // starting at 1. // Let's keep this consistent with the API for now. fields.put("signer_roles[" + (i + 1) + "][name]", s); @@ -284,11 +299,10 @@ public Map getPostFields() throws HelloSignException { } if (isTestMode()) { - fields.put(REQUEST_TEST_MODE, true); + fields.put(REQUEST_TEST_MODE, true); } } catch (Exception ex) { - throw new HelloSignException( - "Could not extract form fields from TemplateDraft.", ex); + throw new HelloSignException("Could not extract form fields from TemplateDraft.", ex); } return fields; } diff --git a/src/main/java/com/hellosign/sdk/resource/TemplateSignatureRequest.java b/src/main/java/com/hellosign/sdk/resource/TemplateSignatureRequest.java index 187ae429..36952cff 100644 --- a/src/main/java/com/hellosign/sdk/resource/TemplateSignatureRequest.java +++ b/src/main/java/com/hellosign/sdk/resource/TemplateSignatureRequest.java @@ -12,8 +12,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -37,11 +37,11 @@ import com.hellosign.sdk.resource.support.Signer; /** - * Represents a HelloSign signature request based on one or more Templates. + * Represents a HelloSign signature request based on one or more Templates. * - * Unlike the SignatureRequest, this object is only used to submit - * the request. A successfully submitted TemplateSignatureRequest will - * return a SignatureRequest object from the server. + * Unlike the SignatureRequest, this object is only used to submit the request. + * A successfully submitted TemplateSignatureRequest will return a + * SignatureRequest object from the server. * * @author "Chris Paul (chris@hellosign.com)" */ @@ -57,9 +57,9 @@ public class TemplateSignatureRequest extends AbstractRequest { // Signers, CC email addresses and custom fields are required // to have an associated role. We'll manage these in a Map, - // as opposed to storing them on the JSON object like other - // fields are stored, so we can support this association. - private Map signers = new HashMap(); + // as opposed to storing them on the JSON object like other + // fields are stored, so we can support this association. + private Map signers = new HashMap(); private Map ccs = new HashMap(); private List customFields = new ArrayList(); @@ -69,8 +69,10 @@ public TemplateSignatureRequest() { /** * Convenience constructor that accepts a single Template. + * * @param template Template - * @throws HelloSignException thrown if there is a problem adding the template ID. + * @throws HelloSignException thrown if there is a problem adding the + * template ID. */ public TemplateSignatureRequest(Template template) throws HelloSignException { this(); @@ -79,8 +81,10 @@ public TemplateSignatureRequest(Template template) throws HelloSignException { /** * Convenience constructor that accepts a list of Templates. + * * @param templates List - * @throws HelloSignException thrown if there is a problem adding the template ID. + * @throws HelloSignException thrown if there is a problem adding the + * template ID. */ public TemplateSignatureRequest(List