{"action":"created","comment":{"url":"https://api.github.com/repos/cloudant/java-cloudant/pulls/comments/44000801","id":44000801,"diff_hunk":"@@ -0,0 +1,545 @@\n+package com.cloudant.client.api;\n+\n+import com.cloudant.client.api.model.Index;\n+import com.cloudant.client.api.model.Permissions;\n+import com.cloudant.client.api.model.Shard;\n+import com.cloudant.client.api.views.Key;\n+import com.cloudant.client.org.lightcouch.CouchDbException;\n+import com.cloudant.client.org.lightcouch.CouchDbProperties;\n+import com.cloudant.client.org.lightcouch.internal.URIBuilder;\n+import com.cloudant.http.HttpConnectionInterceptor;\n+import com.cloudant.http.HttpConnectionRequestInterceptor;\n+import com.cloudant.http.HttpConnectionResponseInterceptor;\n+import com.cloudant.http.interceptors.CookieInterceptor;\n+import com.cloudant.http.interceptors.ProxyAuthInterceptor;\n+import com.cloudant.http.interceptors.SSLCustomizerInterceptor;\n+import com.cloudant.http.interceptors.TimeoutCustomizationInterceptor;\n+import com.google.gson.GsonBuilder;\n+import com.google.gson.reflect.TypeToken;\n+\n+import java.net.MalformedURLException;\n+import java.net.URL;\n+import java.util.ArrayList;\n+import java.util.EnumSet;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.concurrent.TimeUnit;\n+\n+import javax.net.ssl.SSLSocketFactory;\n+\n+/**\n+ * This class builds new {@link CloudantClient} instances.\n+ *\n+ * <h2>Create a new CloudantClient instance for a Cloudant account</h2>\n+ * <pre>\n+ * {@code\n+ * CloudantClient client = ClientBuilder.account(\"yourCloudantAccount\")\n+ *                          .username(\"yourUsername\")\n+ *                          .password(\"yourPassword\")\n+ *                          .build();\n+ * }\n+ * </pre>\n+ *\n+ * <h2>Create a new CloudantClient instance for a Cloudant Local</h2>\n+ * <pre>\n+ * {@code\n+ * CloudantClient client = ClientBuilder.url(new URL(\"https://yourCloudantLocalAddress.example\"))\n+ *                          .username(\"yourUsername\")\n+ *                          .password(\"yourPassword\")\n+ *                          .build();\n+ * }\n+ * </pre>\n+ *\n+ * <h2>Examples creating instances with additional options</h2>\n+ * <h3>Configure a proxy server</h3>\n+ * <pre>\n+ * {@code\n+ * CloudantClient client = ClientBuilder.account(\"yourCloudantAccount\")\n+ *                          .username(\"yourUsername\")\n+ *                          .password(\"yourPassword\")\n+ *                          .proxyURL(new URL(\"https://yourProxyServerAddress.example\"))\n+ *                          .proxyUser(yourProxyUser)\n+ *                          .proxyPassword(yourProxyPass)\n+ *                          .build();\n+ * }\n+ * </pre>\n+ *\n+ * <h3>Client with a custom SSL socket factory</h3>\n+ * <pre>\n+ * {@code\n+ * CloudantClient client = ClientBuilder.account(\"yourCloudantAccount\")\n+ *                          .username(\"yourUsername\")\n+ *                          .password(\"yourPassword\")\n+ *                          .customSSLSocketFactory(...)\n+ *                          .build();\n+ * }\n+ * </pre>\n+ *\n+ * <h3>Client with custom connection and read timeouts</h3>\n+ * <pre>\n+ * {@code\n+ * CloudantClient client = ClientBuilder.account(\"yourCloudantAccount\")\n+ *                          .username(\"yourUsername\")\n+ *                          .password(\"yourPassword\")\n+ *                          .connectionTimeout(ClientBuilder.TimeoutOption.minutes(1))\n+ *                          .readTimeout(ClientBuilder.TimeoutOption.minutes(1))\n+ *                          .build();\n+ * }\n+ * </pre>\n+ *\n+ * @since 2.0.0\n+ */\n+public class ClientBuilder {\n+\n+    /**\n+     * Default max of 6 connections\n+     **/\n+    public static final int DEFAULT_MAX_CONNECTIONS = 6;\n+    /**\n+     * Connection timeout defaults to 5 minutes\n+     **/\n+    public static final TimeoutOption DEFAULT_CONNECTION_TIMEOUT\n+            = TimeoutOption.minutes(5);\n+    /**\n+     * Read timeout defaults to 5 minutes\n+     **/\n+    public static final TimeoutOption DEFAULT_READ_TIMEOUT\n+            = TimeoutOption.minutes(5);\n+\n+    private List<HttpConnectionRequestInterceptor> requestInterceptors = new ArrayList\n+            <HttpConnectionRequestInterceptor>();\n+    private List<HttpConnectionResponseInterceptor> responseInterceptors = new ArrayList\n+            <HttpConnectionResponseInterceptor>();\n+    private String password;\n+    private String username;\n+    private URL url;\n+    private GsonBuilder gsonBuilder;\n+    /**\n+     * Defaults to {@link #DEFAULT_MAX_CONNECTIONS}\n+     **/\n+    private int maxConnections = DEFAULT_MAX_CONNECTIONS;\n+    private URL proxyURL;\n+    private String proxyUser;\n+    private String proxyPassword;\n+    private boolean isSSLAuthenticationDisabled;\n+    private SSLSocketFactory authenticatedModeSSLSocketFactory;\n+    private TimeoutOption connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;\n+    private TimeoutOption readTimeout = DEFAULT_READ_TIMEOUT;\n+\n+    /**\n+     * Constructs a new ClientBuilder for building a CloudantClient instance to connect to the\n+     * Cloudant server with the specified account.\n+     *\n+     * @param account the Cloudant account name to connect to e.g. \"example\" is the account name\n+     *                for the \"example.cloudant.com\" endpoint\n+     * @return a new ClientBuilder for the account\n+     * @throws IllegalArgumentException if the specified account name forms an invalid endpoint URL\n+     */\n+    public static ClientBuilder account(String account) {\n+        try {\n+            URL url = new URL(String.format(\"https://%s.cloudant.com\", account));\n+            return ClientBuilder.url(url);\n+        } catch (MalformedURLException e) {\n+            throw new IllegalArgumentException(\"Could not generate url from account name.\", e);\n+        }\n+    }\n+\n+    /**\n+     * Constructs a new ClientBuilder for building a CloudantClient instance to connect to the\n+     * Cloudant server with the specified URL.\n+     *\n+     * @param url server URL e.g. \"https://yourCloudantLocalAddress.example\"\n+     * @return a new ClientBuilder for the account\n+     */\n+    public static ClientBuilder url(URL url) {\n+        return new ClientBuilder(url);\n+    }\n+\n+\n+    private ClientBuilder(URL url) {\n+        //Check if port exists\n+        int port = url.getPort();\n+        if (port < 0) {\n+            port = url.getDefaultPort();\n+        }\n+        if (url.getUserInfo() != null) {\n+            //Get username and password and replace credential variables\n+            this.username = url.getUserInfo().substring(0, url\n+                    .getUserInfo()\n+                    .indexOf(\":\"));\n+            this.password = url.getUserInfo().substring(url\n+                    .getUserInfo()\n+                    .indexOf(\":\"));\n+        }\n+\n+        try {\n+            //Remove user credentials from url\n+            this.url = URIBuilder.buildUri()\n+                    .scheme(url.getProtocol())\n+                    .host(url.getHost())\n+                    .port(port)\n+                    .build().toURL();\n+        } catch (MalformedURLException e) {\n+            throw new RuntimeException(e);\n+        }\n+    }\n+\n+    /**\n+     * Build the {@link CloudantClient} instance based on the endpoint used to construct this\n+     * client builder and the options that have been set on it before calling this method.\n+     *\n+     * @return the {@link CloudantClient} instance for the specified end point and options\n+     */\n+    public CloudantClient build() {\n+\n+        //Build properties and couchdb client\n+        CouchDbProperties props = new CouchDbProperties(url.getProtocol(),\n+                url.getHost(), url.getPort());\n+\n+        //Create cookie interceptor\n+        if (this.username != null && this.password != null) {\n+            //make interceptor if both username and password are not null\n+\n+            //Create cookie interceptor and set in HttpConnection interceptors\n+            CookieInterceptor cookieInterceptor = new CookieInterceptor(username, password);\n+\n+            props.addRequestInterceptors(cookieInterceptor);\n+            props.addResponseInterceptors(cookieInterceptor);\n+        } else {\n+            //If username or password is null, throw an exception\n+            if (username != null || password != null) {\n+                //Username and password both have to contain values\n+                throw new CouchDbException(\"Either a username and password must be provided, or \" +\n+                        \"both values must be null. Please check the credentials and try again.\");\n+            }\n+        }\n+\n+\n+        //If setter methods for read and connection timeout are not called, default values are used.\n+        props.addRequestInterceptors(new TimeoutCustomizationInterceptor(connectionTimeout,\n+                readTimeout));\n+\n+        //Set connect options\n+        props.setMaxConnections(maxConnections);\n+        props.setProxyURL(proxyURL);\n+        if (proxyUser != null) {\n+            //if there was proxy auth information create an interceptor for it\n+            props.addRequestInterceptors(new ProxyAuthInterceptor(proxyUser,\n+                    proxyPassword));\n+        }\n+        if (isSSLAuthenticationDisabled) {\n+            props.addRequestInterceptors(SSLCustomizerInterceptor\n+                    .SSL_AUTH_DISABLED_INTERCEPTOR);\n+        }\n+        if (authenticatedModeSSLSocketFactory != null) {\n+            props.addRequestInterceptors(new SSLCustomizerInterceptor(\n+                    authenticatedModeSSLSocketFactory\n+            ));\n+        }\n+\n+        //Set http connection interceptors\n+        if (requestInterceptors != null) {\n+            for (HttpConnectionRequestInterceptor requestInterceptor : requestInterceptors) {\n+                props.addRequestInterceptors(requestInterceptor);\n+            }\n+        }\n+        if (responseInterceptors != null) {\n+            for (HttpConnectionResponseInterceptor responseInterceptor : responseInterceptors) {\n+                props.addResponseInterceptors(responseInterceptor);\n+            }\n+        }\n+\n+        return new CloudantClient(props, gsonBuilder);\n+    }\n+\n+    /**\n+     * Sets a username or API key for the client connection.\n+     *\n+     * @param username the user or API key for the session\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder username(String username) {\n+        this.username = username;\n+        return this;\n+    }\n+\n+    /**\n+     * Sets the password for the client connection. The password is the one for the username or\n+     * API key set by the {@link #username(String)} method.\n+     *\n+     * @param password user password or API key passphrase\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder password(String password) {\n+        this.password = password;\n+        return this;\n+    }\n+\n+    /**\n+     * Set a custom GsonBuilder to use when serializing and de-serializing requests and\n+     * responses between the CloudantClient and the server.\n+     * <P>\n+     * Note: the supplied GsonBuilder will be augmented with some internal TypeAdapters.\n+     * </P>\n+     *\n+     * @param gsonBuilder the custom GsonBuilder to use\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder gsonBuilder(GsonBuilder gsonBuilder) {\n+        //register additional cloudant deserializers and then let lightcouch init too\n+        gsonBuilder.registerTypeAdapter(new TypeToken<List<Shard>>() {\n+        }.getType(), new ShardDeserializer())\n+                .registerTypeAdapter(new TypeToken<List<Index>>() {\n+                }.getType(), new IndexDeserializer())\n+                .registerTypeAdapter(new TypeToken<Map<String, EnumSet<Permissions>>>() {\n+                        }.getType(),\n+                        new SecurityDeserializer())\n+                .registerTypeAdapter(Key.ComplexKey.class, new Key.ComplexKeyDeserializer());\n+        this.gsonBuilder = gsonBuilder;\n+        return this;\n+    }\n+\n+    /**\n+     * Set the maximum number of connections to maintain in the connection pool.\n+     * <P>\n+     * Note: this setting only applies if using the optional OkHttp dependency. If OkHttp is not\n+     * present then the JVM configuration is used for pooling. Consult the JVM documentation for\n+     * the {@code http.maxConnections} property for further details.\n+     * </P>\n+     * Defaults to {@link #DEFAULT_MAX_CONNECTIONS}\n+     *\n+     * @param maxConnections the maximum number of simultaneous connections to open to the server\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder maxConnections(int maxConnections) {\n+        this.maxConnections = maxConnections;\n+        return this;\n+    }\n+\n+    /**\n+     * Sets a proxy url for the client connection.\n+     *\n+     * @param proxyURL the URL of the proxy server\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder proxyURL(URL proxyURL) {\n+        this.proxyURL = proxyURL;\n+        return this;\n+    }\n+\n+    /**\n+     * Sets an optional proxy username for the client connection.\n+     *\n+     * @param proxyUser username for the proxy server\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder proxyUser(String proxyUser) {\n+        this.proxyUser = proxyUser;\n+        return this;\n+    }\n+\n+    /**\n+     * Sets an optional proxy password for the proxy user specified by\n+     * {@link #proxyUser(String)}.\n+     *\n+     * @param proxyPassword password for the proxy server user\n+     * @return this ClientBuilder object for setting additional options\n+     */\n+    public ClientBuilder proxyPassword(String proxyPassword) {\n+        this.proxyPassword = proxyPassword;\n+        return this;\n+    }\n+\n+    /**\n+     * Flag to disable hostname verification and certificate chain validation. This is not\n+     * recommended, but for example could be useful for testing with a self-signed certificate.\n+     * <P>\n+     * The SSL authentication is enabled by default meaning that hostname verification\n+     * and certificate chain validation is done using the JVM default settings.\n+     * </P>\n+     *\n+     * @return this ClientBuilder object for setting additional options\n+     * @throws IllegalStateException if {@link #customSSLSocketFactory(SSLSocketFactory)}\n+     * has been called on this ClientBuilder\n+     * @see #customSSLSocketFactory(SSLSocketFactory)\n+     */\n+    public ClientBuilder disableSSLAuthentication() {\n+        if(authenticatedModeSSLSocketFactory == null) {\n+            this.isSSLAuthenticationDisabled = true;\n+        } else {\n+            throw new IllegalStateException(\"Cannot disable SSL authentication when a \" +\n+                    \"custom SSLSocketFactory has been set.\");\n+        }\n+        return this;\n+    }\n+    \n+\n+    /**\n+     * Specifies the custom SSLSocketFactory to use when connecting to Cloudant over a\n+     * <code>https</code> URL, when SSL authentication is enabled.\n+     *\n+     * @param factory An SSLSocketFactory, or <code>null</code> for the\n+     *                default SSLSocketFactory of the JRE.\n+     * @return this ClientBuilder object for setting additional options\n+     * @throws IllegalStateException if {@link #disableSSLAuthentication()}\n+     * has been called on this ClientBuilder\n+     * @see #disableSSLAuthentication()\n+     */\n+    public ClientBuilder customSSLSocketFactory(SSLSocketFactory factory) {\n+        if(!isSSLAuthenticationDisabled) {\n+            this.authenticatedModeSSLSocketFactory = factory;\n+        } else {\n+            throw new IllegalStateException(\"Cannot use a custom SSLSocketFactory when \" +\n+                    \"SSL authentication is disabled.\");\n+        }\n+        return this;\n+    }\n+\n+    /**\n+     * This method adds {@link HttpConnectionInterceptor}s to be used on the CloudantClient\n+     * connection. Interceptors can be used to modify the HTTP requests and responses between the\n+     * CloudantClient and the server.\n+     * <P>\n+     * An example interceptor use might be to apply a custom authorization mechanism. For\n+     * instance to use BasicAuth instead of CookieAuth it is possible to add a\n+     * {@link com.cloudant.http.interceptors.BasicAuthInterceptor}:\n+     * </P>\n+     * <pre>\n+     * {@code\n+     * CloudantClient client = ClientBuilder.account(\"yourCloudantAccount\")\n+     *      .interceptors(new BasicAuthInterceptor(\"yourUsername:yourPassword\"))\n+     *      .build()\n+     * }\n+     * </pre>\n+     *\n+     * @param interceptors one or more HttpConnectionInterceptor objects\n+     * @return this ClientBuilder object for setting additional options\n+     * @see HttpConnectionInterceptor\n+     */\n+    public ClientBuilder interceptors(HttpConnectionInterceptor... interceptors) {","path":"src/main/java/com/cloudant/client/api/ClientBuilder.java","position":425,"original_position":419,"commit_id":"2382f0c7f948b191a8c15bda13cdf6df5a054edc","original_commit_id":"89ab9919cc93664478f00f35a909b39ac08ec372","user":{"login":"rhyshort","id":1616727,"avatar_url":"https://avatars.githubusercontent.com/u/1616727?v=3","gravatar_id":"","url":"https://api.github.com/users/rhyshort","html_url":"https://github.com/rhyshort","followers_url":"https://api.github.com/users/rhyshort/followers","following_url":"https://api.github.com/users/rhyshort/following{/other_user}","gists_url":"https://api.github.com/users/rhyshort/gists{/gist_id}","starred_url":"https://api.github.com/users/rhyshort/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rhyshort/subscriptions","organizations_url":"https://api.github.com/users/rhyshort/orgs","repos_url":"https://api.github.com/users/rhyshort/repos","events_url":"https://api.github.com/users/rhyshort/events{/privacy}","received_events_url":"https://api.github.com/users/rhyshort/received_events","type":"User","site_admin":false},"body":"I don't think its necessary, just a nice to have, I'm fine with leaving it with just a var args method, we can always add it later if users ask for it.","created_at":"2015-11-05T11:18:16Z","updated_at":"2015-11-05T11:18:16Z","html_url":"https://github.com/cloudant/java-cloudant/pull/129#discussion_r44000801","pull_request_url":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129","_links":{"self":{"href":"https://api.github.com/repos/cloudant/java-cloudant/pulls/comments/44000801"},"html":{"href":"https://github.com/cloudant/java-cloudant/pull/129#discussion_r44000801"},"pull_request":{"href":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129"}}},"pull_request":{"url":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129","id":49229406,"html_url":"https://github.com/cloudant/java-cloudant/pull/129","diff_url":"https://github.com/cloudant/java-cloudant/pull/129.diff","patch_url":"https://github.com/cloudant/java-cloudant/pull/129.patch","issue_url":"https://api.github.com/repos/cloudant/java-cloudant/issues/129","number":129,"state":"open","locked":false,"title":"Logical Cloudant client constructors","user":{"login":"emlaver","id":10552966,"avatar_url":"https://avatars.githubusercontent.com/u/10552966?v=3","gravatar_id":"","url":"https://api.github.com/users/emlaver","html_url":"https://github.com/emlaver","followers_url":"https://api.github.com/users/emlaver/followers","following_url":"https://api.github.com/users/emlaver/following{/other_user}","gists_url":"https://api.github.com/users/emlaver/gists{/gist_id}","starred_url":"https://api.github.com/users/emlaver/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emlaver/subscriptions","organizations_url":"https://api.github.com/users/emlaver/orgs","repos_url":"https://api.github.com/users/emlaver/repos","events_url":"https://api.github.com/users/emlaver/events{/privacy}","received_events_url":"https://api.github.com/users/emlaver/received_events","type":"User","site_admin":false},"body":"*What*\r\nRe-design CloudantClient constructors with new client builder that can handle full account URL or account name.  Builder also includes setting HTTP interceptors and connection options (connect options removed). Example:\r\n\r\n```\r\nClientBuilder.url(new URL(“http://localhost”))\r\n\t.setConnectionTimeout(ClientBuilder.TimeoutOption\r\n\t\t.minutes(1))\r\n\t.setReadTimeout(ClientBuilder.TimeoutOption\r\n\t\t.minutes(3))\r\n\t.build();\r\n```\r\nWith Cloudant account:\r\n```\r\nClientBuilder.account(“emlaver”)\r\n\t.username(“emlaver\")\r\n\t.password(“password”)\r\n\t.build();\r\n```\r\n\r\n*How*\r\n- ClientBuilder class to replace client constructor\r\n   - Use URL or account name for initial client\r\n   - Remove public CloudantClient constructors\r\n   - Call new protected constructor from ClientBuilder\r\n   - Move setter method for connection options from ConnectOptions\r\n   - Move TimeoutOption inner class to ClientBuilder\r\n- Remove ConnectOptions class\r\n- Move default values for connect options and interceptors into ClientBuilder\r\n- Remove account and getter method from CloudantClient\r\n- Add helper methods for http(s) test address testing and creating URL for simple http server\r\n- Add SERVER_URL in client helper\r\n- Add HttpConnectionInterceptor super class\r\n- Extend request and response interceptor to new super class\r\n\r\n*Testing*\r\nUpdate existing test cases with new ClientBuilder design.\r\n\r\nreviewer @ricellis\r\nreviewer @rhyshort ","created_at":"2015-10-30T04:07:39Z","updated_at":"2015-11-05T11:18:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ea08ae64c2e87eee8cef8ed187a80fb15f3cf2bd","assignee":null,"milestone":{"url":"https://api.github.com/repos/cloudant/java-cloudant/milestones/2","html_url":"https://github.com/cloudant/java-cloudant/milestones/2.0","labels_url":"https://api.github.com/repos/cloudant/java-cloudant/milestones/2/labels","id":1363461,"number":2,"title":"2.0","description":"","creator":{"login":"ricellis","id":10698320,"avatar_url":"https://avatars.githubusercontent.com/u/10698320?v=3","gravatar_id":"","url":"https://api.github.com/users/ricellis","html_url":"https://github.com/ricellis","followers_url":"https://api.github.com/users/ricellis/followers","following_url":"https://api.github.com/users/ricellis/following{/other_user}","gists_url":"https://api.github.com/users/ricellis/gists{/gist_id}","starred_url":"https://api.github.com/users/ricellis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ricellis/subscriptions","organizations_url":"https://api.github.com/users/ricellis/orgs","repos_url":"https://api.github.com/users/ricellis/repos","events_url":"https://api.github.com/users/ricellis/events{/privacy}","received_events_url":"https://api.github.com/users/ricellis/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":21,"state":"open","created_at":"2015-10-19T09:17:28Z","updated_at":"2015-11-02T16:48:01Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129/commits","review_comments_url":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129/comments","review_comment_url":"https://api.github.com/repos/cloudant/java-cloudant/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cloudant/java-cloudant/issues/129/comments","statuses_url":"https://api.github.com/repos/cloudant/java-cloudant/statuses/2382f0c7f948b191a8c15bda13cdf6df5a054edc","head":{"label":"cloudant:47709-logical-client-constructors-02","ref":"47709-logical-client-constructors-02","sha":"2382f0c7f948b191a8c15bda13cdf6df5a054edc","user":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"repo":{"id":22873328,"name":"java-cloudant","full_name":"cloudant/java-cloudant","owner":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cloudant/java-cloudant","description":"A Java client for Cloudant","fork":false,"url":"https://api.github.com/repos/cloudant/java-cloudant","forks_url":"https://api.github.com/repos/cloudant/java-cloudant/forks","keys_url":"https://api.github.com/repos/cloudant/java-cloudant/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudant/java-cloudant/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudant/java-cloudant/teams","hooks_url":"https://api.github.com/repos/cloudant/java-cloudant/hooks","issue_events_url":"https://api.github.com/repos/cloudant/java-cloudant/issues/events{/number}","events_url":"https://api.github.com/repos/cloudant/java-cloudant/events","assignees_url":"https://api.github.com/repos/cloudant/java-cloudant/assignees{/user}","branches_url":"https://api.github.com/repos/cloudant/java-cloudant/branches{/branch}","tags_url":"https://api.github.com/repos/cloudant/java-cloudant/tags","blobs_url":"https://api.github.com/repos/cloudant/java-cloudant/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudant/java-cloudant/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudant/java-cloudant/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudant/java-cloudant/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudant/java-cloudant/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudant/java-cloudant/languages","stargazers_url":"https://api.github.com/repos/cloudant/java-cloudant/stargazers","contributors_url":"https://api.github.com/repos/cloudant/java-cloudant/contributors","subscribers_url":"https://api.github.com/repos/cloudant/java-cloudant/subscribers","subscription_url":"https://api.github.com/repos/cloudant/java-cloudant/subscription","commits_url":"https://api.github.com/repos/cloudant/java-cloudant/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudant/java-cloudant/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudant/java-cloudant/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudant/java-cloudant/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudant/java-cloudant/contents/{+path}","compare_url":"https://api.github.com/repos/cloudant/java-cloudant/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudant/java-cloudant/merges","archive_url":"https://api.github.com/repos/cloudant/java-cloudant/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudant/java-cloudant/downloads","issues_url":"https://api.github.com/repos/cloudant/java-cloudant/issues{/number}","pulls_url":"https://api.github.com/repos/cloudant/java-cloudant/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudant/java-cloudant/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudant/java-cloudant/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudant/java-cloudant/labels{/name}","releases_url":"https://api.github.com/repos/cloudant/java-cloudant/releases{/id}","created_at":"2014-08-12T10:19:56Z","updated_at":"2015-08-29T22:23:01Z","pushed_at":"2015-11-05T10:29:36Z","git_url":"git://github.com/cloudant/java-cloudant.git","ssh_url":"git@github.com:cloudant/java-cloudant.git","clone_url":"https://github.com/cloudant/java-cloudant.git","svn_url":"https://github.com/cloudant/java-cloudant","homepage":null,"size":4948,"stargazers_count":14,"watchers_count":14,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":17,"mirror_url":null,"open_issues_count":8,"forks":17,"open_issues":8,"watchers":14,"default_branch":"master"}},"base":{"label":"cloudant:master","ref":"master","sha":"2c55047533fe9c0f2fdc68fc905a3ab9d931bc11","user":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"repo":{"id":22873328,"name":"java-cloudant","full_name":"cloudant/java-cloudant","owner":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cloudant/java-cloudant","description":"A Java client for Cloudant","fork":false,"url":"https://api.github.com/repos/cloudant/java-cloudant","forks_url":"https://api.github.com/repos/cloudant/java-cloudant/forks","keys_url":"https://api.github.com/repos/cloudant/java-cloudant/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudant/java-cloudant/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudant/java-cloudant/teams","hooks_url":"https://api.github.com/repos/cloudant/java-cloudant/hooks","issue_events_url":"https://api.github.com/repos/cloudant/java-cloudant/issues/events{/number}","events_url":"https://api.github.com/repos/cloudant/java-cloudant/events","assignees_url":"https://api.github.com/repos/cloudant/java-cloudant/assignees{/user}","branches_url":"https://api.github.com/repos/cloudant/java-cloudant/branches{/branch}","tags_url":"https://api.github.com/repos/cloudant/java-cloudant/tags","blobs_url":"https://api.github.com/repos/cloudant/java-cloudant/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudant/java-cloudant/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudant/java-cloudant/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudant/java-cloudant/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudant/java-cloudant/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudant/java-cloudant/languages","stargazers_url":"https://api.github.com/repos/cloudant/java-cloudant/stargazers","contributors_url":"https://api.github.com/repos/cloudant/java-cloudant/contributors","subscribers_url":"https://api.github.com/repos/cloudant/java-cloudant/subscribers","subscription_url":"https://api.github.com/repos/cloudant/java-cloudant/subscription","commits_url":"https://api.github.com/repos/cloudant/java-cloudant/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudant/java-cloudant/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudant/java-cloudant/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudant/java-cloudant/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudant/java-cloudant/contents/{+path}","compare_url":"https://api.github.com/repos/cloudant/java-cloudant/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudant/java-cloudant/merges","archive_url":"https://api.github.com/repos/cloudant/java-cloudant/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudant/java-cloudant/downloads","issues_url":"https://api.github.com/repos/cloudant/java-cloudant/issues{/number}","pulls_url":"https://api.github.com/repos/cloudant/java-cloudant/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudant/java-cloudant/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudant/java-cloudant/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudant/java-cloudant/labels{/name}","releases_url":"https://api.github.com/repos/cloudant/java-cloudant/releases{/id}","created_at":"2014-08-12T10:19:56Z","updated_at":"2015-08-29T22:23:01Z","pushed_at":"2015-11-05T10:29:36Z","git_url":"git://github.com/cloudant/java-cloudant.git","ssh_url":"git@github.com:cloudant/java-cloudant.git","clone_url":"https://github.com/cloudant/java-cloudant.git","svn_url":"https://github.com/cloudant/java-cloudant","homepage":null,"size":4948,"stargazers_count":14,"watchers_count":14,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":17,"mirror_url":null,"open_issues_count":8,"forks":17,"open_issues":8,"watchers":14,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129"},"html":{"href":"https://github.com/cloudant/java-cloudant/pull/129"},"issue":{"href":"https://api.github.com/repos/cloudant/java-cloudant/issues/129"},"comments":{"href":"https://api.github.com/repos/cloudant/java-cloudant/issues/129/comments"},"review_comments":{"href":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129/comments"},"review_comment":{"href":"https://api.github.com/repos/cloudant/java-cloudant/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cloudant/java-cloudant/pulls/129/commits"},"statuses":{"href":"https://api.github.com/repos/cloudant/java-cloudant/statuses/2382f0c7f948b191a8c15bda13cdf6df5a054edc"}}}}