{"action":"created","comment":{"url":"https://api.github.com/repos/cloudant/sync-android/pulls/comments/44916572","id":44916572,"diff_hunk":"@@ -0,0 +1,210 @@\n+/**\n+ * Copyright (c) 2013 Cloudant, Inc. All rights reserved.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file\n+ * except in compliance with the License. You may obtain a copy of the License at\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software distributed under the\n+ * License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n+ * either express or implied. See the License for the specific language governing permissions\n+ * and limitations under the License.\n+ */\n+\n+package com.cloudant.sync.replication;\n+\n+import com.cloudant.sync.datastore.DocumentRevsList;\n+import com.google.common.base.Preconditions;\n+\n+import java.util.Iterator;\n+import java.util.List;\n+import java.util.concurrent.Callable;\n+import java.util.concurrent.ExecutionException;\n+import java.util.concurrent.ExecutorCompletionService;\n+import java.util.concurrent.Future;\n+import java.util.concurrent.LinkedBlockingQueue;\n+import java.util.concurrent.ThreadPoolExecutor;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.atomic.AtomicInteger;\n+import java.util.logging.Logger;\n+\n+/**\n+ * Handles calling CouchClient.getDocWithOpenRevisions() for a batch of revisions IDs, with\n+ * multiple simultaneous HTTP threads, returning the results back in a manner which can be iterated\n+ * over (to avoid deserialising the entire result into memory).\n+ * \n+ * For each revision ID, gets the revision tree for a given document ID and lists of open revision IDs\n+ * and \"atts_since\" (revision IDs for which we know we have attachments)\n+ *\n+ * The document id and open revisions are from one row of change feeds. For example, for the\n+ * following change feed:\n+ *\n+ * {\n+ *   \"last_seq\": 35,\n+ *   \"results\": [\n+ *     { \"changes\":\n+ *       [ { \"rev\": \"1-bd42b942b8b672f0289cf3cd1f67044c\" } ],\n+ *       \"id\": \"2013-09-23T20:50:56.251Z\",\n+ *       \"seq\": 27\n+ *     },\n+ *     { \"changes\":\n+ *       [ { \"rev\": \"29-3f4dabfb32290e557ac1d16b2e8f069c\" },\n+ *         { \"rev\": \"29-01fcbf8a3f1457eff21e18f7766d3b45\" },\n+ *         { \"rev\": \"26-30722da17ad35cf1860f126dba391d67\" }\n+ *       ],\n+ *       \"id\": \"2013-09-10T17:47:17.770Z\",\n+ *       \"seq\": 35\n+ *       }\n+ *    ]\n+ * }\n+ *\n+ * For document with id \"2013-09-10T17:47:17.770Z\", it has open revisions:\n+ *\n+ *       [\n+ *         { \"rev\": \"29-3f4dabfb32290e557ac1d16b2e8f069c\" },\n+ *         { \"rev\": \"29-01fcbf8a3f1457eff21e18f7766d3b45\" },\n+ *         { \"rev\": \"26-30722da17ad35cf1860f126dba391d67\" }\n+ *       ]\n+ */\n+class GetRevisionTaskThreaded implements Iterable<DocumentRevsList> {\n+\n+    // TODO logging\n+    private static final Logger logger = Logger.getLogger(GetRevisionTaskThreaded.class.getCanonicalName());\n+\n+    // members used to make requests:\n+\n+    private final ThreadPoolExecutor executorService;\n+    private final ExecutorCompletionService<DocumentRevsList> completionService;\n+    private final CouchDB sourceDb;\n+    private final List<BulkGetRequest> requests;\n+    private final boolean pullAttachmentsInline;\n+\n+    private boolean iteratorValid = true;\n+\n+    int threads = 4; // TODO - config?\n+\n+    // members used to handle responses:\n+\n+    LinkedBlockingQueue<Future<DocumentRevsList>> responses;\n+    AtomicInteger requestsOutstanding;\n+\n+    public GetRevisionTaskThreaded(CouchDB sourceDb,\n+                                   List<BulkGetRequest> requests,\n+                                   boolean pullAttachmentsInline) {\n+        Preconditions.checkNotNull(sourceDb, \"sourceDb cannot be null\");\n+        Preconditions.checkNotNull(requests, \"requests cannot be null\");\n+        for(BulkGetRequest request : requests) {\n+            Preconditions.checkNotNull(request.id, \"id cannot be null\");\n+            Preconditions.checkNotNull(request.revs, \"revs cannot be null\");\n+        }\n+\n+        this.sourceDb = sourceDb;\n+        this.requests = requests;\n+        this.pullAttachmentsInline = pullAttachmentsInline;\n+        this.executorService = new ThreadPoolExecutor(threads, threads, 1, TimeUnit.MINUTES,\n+                new LinkedBlockingQueue<Runnable>());\n+        // limit the size of the response queue, so we don't produce thousands of results before\n+        // they have been consumed\n+        this.responses = new LinkedBlockingQueue<Future<DocumentRevsList>>(threads) {\n+            // from javadoc of ExecutorCompletionService:\n+            // \"This queue is treated as unbounded -- failed attempted Queue.add operations for completed taskes cause them not to be retrievable\"\n+            // so we make add() behave like offer() so we can block until the queue has capacity\n+            @Override\n+            public boolean add(Future<DocumentRevsList> documentRevsListFuture) {\n+                boolean offerResult;\n+                try {\n+                    offerResult = offer(documentRevsListFuture, 5, TimeUnit.MINUTES);\n+                } catch (InterruptedException ie) {\n+                    throw new RuntimeException(\"Offer interrupted\", ie);\n+                }\n+                if (!offerResult) {\n+                    throw new RuntimeException(\"Offer timed out\");\n+                }\n+                return true;\n+            }\n+        };\n+        this.completionService = new ExecutorCompletionService<DocumentRevsList>(executorService, responses);\n+        this.requestsOutstanding = new AtomicInteger(requests.size());\n+\n+        // we make the request at construction time...\n+        for (final BulkGetRequest request : requests) {\n+\n+            completionService.submit(new Callable<DocumentRevsList>() {\n+                @Override\n+                public DocumentRevsList call() throws Exception {\n+                    return new DocumentRevsList(GetRevisionTaskThreaded.this.sourceDb.getRevisions\n+                            (request.id, request.revs, request.atts_since,\n+                                    GetRevisionTaskThreaded.this\n+                                            .pullAttachmentsInline));\n+                }\n+            });\n+        }\n+    }\n+\n+    @Override\n+    public Iterator<DocumentRevsList> iterator() {\n+        return new GetRevisionTaskIterator();\n+    }\n+\n+    @Override\n+    public String toString() {\n+        return \"GetRevisionTask{\" +\n+                \"sourceDb=\" + sourceDb +\n+                \", requests=\" + requests +\n+                \", pullAttachmentsInline=\" + pullAttachmentsInline +\n+                '}';\n+    }\n+\n+    private class GetRevisionTaskIterator implements Iterator<DocumentRevsList> {\n+\n+        @Override\n+        public boolean hasNext() {\n+            // can't advance iterator as there was a problem in call()\n+            if (!iteratorValid) {\n+                return false;\n+            }\n+\n+            boolean next = requestsOutstanding.get() != 0;\n+\n+            // if we have returned all of our results, we can shut down the executor\n+            if (!next) {\n+                iteratorValid = false;\n+                executorService.shutdown();\n+                try {\n+                    executorService.awaitTermination(5, TimeUnit.SECONDS);\n+                } catch (InterruptedException e) {\n+                    ;\n+                }\n+            }\n+\n+            return next;\n+        }\n+\n+        @Override\n+        public DocumentRevsList next() {\n+            // can't advance iterator as there was a problem in call()\n+            if (!iteratorValid) {\n+                return null;","path":"cloudant-sync-datastore-core/src/main/java/com/cloudant/sync/replication/GetRevisionTaskThreaded.java","position":null,"original_position":188,"commit_id":"3a485d4d35c96d7de013cf4e7058855d91813d66","original_commit_id":"2a35d2b390191e809ecea55e12a10c852abb08ba","user":{"login":"tomblench","id":6302842,"avatar_url":"https://avatars.githubusercontent.com/u/6302842?v=3","gravatar_id":"","url":"https://api.github.com/users/tomblench","html_url":"https://github.com/tomblench","followers_url":"https://api.github.com/users/tomblench/followers","following_url":"https://api.github.com/users/tomblench/following{/other_user}","gists_url":"https://api.github.com/users/tomblench/gists{/gist_id}","starred_url":"https://api.github.com/users/tomblench/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomblench/subscriptions","organizations_url":"https://api.github.com/users/tomblench/orgs","repos_url":"https://api.github.com/users/tomblench/repos","events_url":"https://api.github.com/users/tomblench/events{/privacy}","received_events_url":"https://api.github.com/users/tomblench/received_events","type":"User","site_admin":false},"body":"Also done in 9f7bd62","created_at":"2015-11-16T12:13:52Z","updated_at":"2015-11-16T12:13:52Z","html_url":"https://github.com/cloudant/sync-android/pull/211#discussion_r44916572","pull_request_url":"https://api.github.com/repos/cloudant/sync-android/pulls/211","_links":{"self":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/comments/44916572"},"html":{"href":"https://github.com/cloudant/sync-android/pull/211#discussion_r44916572"},"pull_request":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/211"}}},"pull_request":{"url":"https://api.github.com/repos/cloudant/sync-android/pulls/211","id":47672322,"html_url":"https://github.com/cloudant/sync-android/pull/211","diff_url":"https://github.com/cloudant/sync-android/pull/211.diff","patch_url":"https://github.com/cloudant/sync-android/pull/211.patch","issue_url":"https://api.github.com/repos/cloudant/sync-android/issues/211","number":211,"state":"open","locked":false,"title":"Initial work to support _bulk_get","user":{"login":"tomblench","id":6302842,"avatar_url":"https://avatars.githubusercontent.com/u/6302842?v=3","gravatar_id":"","url":"https://api.github.com/users/tomblench","html_url":"https://github.com/tomblench","followers_url":"https://api.github.com/users/tomblench/followers","following_url":"https://api.github.com/users/tomblench/following{/other_user}","gists_url":"https://api.github.com/users/tomblench/gists{/gist_id}","starred_url":"https://api.github.com/users/tomblench/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomblench/subscriptions","organizations_url":"https://api.github.com/users/tomblench/orgs","repos_url":"https://api.github.com/users/tomblench/repos","events_url":"https://api.github.com/users/tomblench/events{/privacy}","received_events_url":"https://api.github.com/users/tomblench/received_events","type":"User","site_admin":false},"body":"This PR adds the necessary internal support for the new `_bulk_get` endpoint.\r\n\r\nWe can switch between `_bulk_get` and \"one document at a time\" whilst providing the same interface to the replication code. Currently only the \"one document at a time\" code path works. A later PR will add the functionality for calling `_bulk_get` and deserialising responses in a streaming/iterable manner.\r\n\r\n* Rename existing CouchClient bulkXXX methods to bulkPostXXX\r\n* Add dummy CouchClient.bulkGet method (implementation to come...)\r\n* Add CouchClientWrapper.bulkGetRevisions - returns an object which\r\n  can be iterated over to get each revision in turn\r\n* Convert GetRevisionTask to use above iterator\r\n\r\nreviewer: @mikerhodes \r\nreviewer: @ricellis ","created_at":"2015-10-14T15:11:41Z","updated_at":"2015-11-16T12:13:52Z","closed_at":null,"merged_at":null,"merge_commit_sha":"57de3e1e6eeebae7612a0f4c8c17cb7a0f444071","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cloudant/sync-android/pulls/211/commits","review_comments_url":"https://api.github.com/repos/cloudant/sync-android/pulls/211/comments","review_comment_url":"https://api.github.com/repos/cloudant/sync-android/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cloudant/sync-android/issues/211/comments","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/3a485d4d35c96d7de013cf4e7058855d91813d66","head":{"label":"cloudant:53075-Support-bulk-get-endpoint","ref":"53075-Support-bulk-get-endpoint","sha":"3a485d4d35c96d7de013cf4e7058855d91813d66","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":16811502,"name":"sync-android","full_name":"cloudant/sync-android","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/sync-android","description":"A JSON-based document datastore for Android applications","fork":false,"url":"https://api.github.com/repos/cloudant/sync-android","forks_url":"https://api.github.com/repos/cloudant/sync-android/forks","keys_url":"https://api.github.com/repos/cloudant/sync-android/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudant/sync-android/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudant/sync-android/teams","hooks_url":"https://api.github.com/repos/cloudant/sync-android/hooks","issue_events_url":"https://api.github.com/repos/cloudant/sync-android/issues/events{/number}","events_url":"https://api.github.com/repos/cloudant/sync-android/events","assignees_url":"https://api.github.com/repos/cloudant/sync-android/assignees{/user}","branches_url":"https://api.github.com/repos/cloudant/sync-android/branches{/branch}","tags_url":"https://api.github.com/repos/cloudant/sync-android/tags","blobs_url":"https://api.github.com/repos/cloudant/sync-android/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudant/sync-android/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudant/sync-android/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudant/sync-android/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudant/sync-android/languages","stargazers_url":"https://api.github.com/repos/cloudant/sync-android/stargazers","contributors_url":"https://api.github.com/repos/cloudant/sync-android/contributors","subscribers_url":"https://api.github.com/repos/cloudant/sync-android/subscribers","subscription_url":"https://api.github.com/repos/cloudant/sync-android/subscription","commits_url":"https://api.github.com/repos/cloudant/sync-android/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudant/sync-android/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudant/sync-android/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudant/sync-android/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudant/sync-android/contents/{+path}","compare_url":"https://api.github.com/repos/cloudant/sync-android/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudant/sync-android/merges","archive_url":"https://api.github.com/repos/cloudant/sync-android/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudant/sync-android/downloads","issues_url":"https://api.github.com/repos/cloudant/sync-android/issues{/number}","pulls_url":"https://api.github.com/repos/cloudant/sync-android/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudant/sync-android/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudant/sync-android/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudant/sync-android/labels{/name}","releases_url":"https://api.github.com/repos/cloudant/sync-android/releases{/id}","created_at":"2014-02-13T17:52:09Z","updated_at":"2015-11-10T04:53:59Z","pushed_at":"2015-11-16T12:10:52Z","git_url":"git://github.com/cloudant/sync-android.git","ssh_url":"git@github.com:cloudant/sync-android.git","clone_url":"https://github.com/cloudant/sync-android.git","svn_url":"https://github.com/cloudant/sync-android","homepage":"","size":31440,"stargazers_count":115,"watchers_count":115,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":36,"mirror_url":null,"open_issues_count":9,"forks":36,"open_issues":9,"watchers":115,"default_branch":"master"}},"base":{"label":"cloudant:master","ref":"master","sha":"25e0bd203b7cb2ace77d6c00687bee6d42fec4c4","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":16811502,"name":"sync-android","full_name":"cloudant/sync-android","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/sync-android","description":"A JSON-based document datastore for Android applications","fork":false,"url":"https://api.github.com/repos/cloudant/sync-android","forks_url":"https://api.github.com/repos/cloudant/sync-android/forks","keys_url":"https://api.github.com/repos/cloudant/sync-android/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudant/sync-android/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudant/sync-android/teams","hooks_url":"https://api.github.com/repos/cloudant/sync-android/hooks","issue_events_url":"https://api.github.com/repos/cloudant/sync-android/issues/events{/number}","events_url":"https://api.github.com/repos/cloudant/sync-android/events","assignees_url":"https://api.github.com/repos/cloudant/sync-android/assignees{/user}","branches_url":"https://api.github.com/repos/cloudant/sync-android/branches{/branch}","tags_url":"https://api.github.com/repos/cloudant/sync-android/tags","blobs_url":"https://api.github.com/repos/cloudant/sync-android/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudant/sync-android/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudant/sync-android/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudant/sync-android/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudant/sync-android/languages","stargazers_url":"https://api.github.com/repos/cloudant/sync-android/stargazers","contributors_url":"https://api.github.com/repos/cloudant/sync-android/contributors","subscribers_url":"https://api.github.com/repos/cloudant/sync-android/subscribers","subscription_url":"https://api.github.com/repos/cloudant/sync-android/subscription","commits_url":"https://api.github.com/repos/cloudant/sync-android/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudant/sync-android/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudant/sync-android/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudant/sync-android/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudant/sync-android/contents/{+path}","compare_url":"https://api.github.com/repos/cloudant/sync-android/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudant/sync-android/merges","archive_url":"https://api.github.com/repos/cloudant/sync-android/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudant/sync-android/downloads","issues_url":"https://api.github.com/repos/cloudant/sync-android/issues{/number}","pulls_url":"https://api.github.com/repos/cloudant/sync-android/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudant/sync-android/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudant/sync-android/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudant/sync-android/labels{/name}","releases_url":"https://api.github.com/repos/cloudant/sync-android/releases{/id}","created_at":"2014-02-13T17:52:09Z","updated_at":"2015-11-10T04:53:59Z","pushed_at":"2015-11-16T12:10:52Z","git_url":"git://github.com/cloudant/sync-android.git","ssh_url":"git@github.com:cloudant/sync-android.git","clone_url":"https://github.com/cloudant/sync-android.git","svn_url":"https://github.com/cloudant/sync-android","homepage":"","size":31440,"stargazers_count":115,"watchers_count":115,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":36,"mirror_url":null,"open_issues_count":9,"forks":36,"open_issues":9,"watchers":115,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/211"},"html":{"href":"https://github.com/cloudant/sync-android/pull/211"},"issue":{"href":"https://api.github.com/repos/cloudant/sync-android/issues/211"},"comments":{"href":"https://api.github.com/repos/cloudant/sync-android/issues/211/comments"},"review_comments":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/211/comments"},"review_comment":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/211/commits"},"statuses":{"href":"https://api.github.com/repos/cloudant/sync-android/statuses/3a485d4d35c96d7de013cf4e7058855d91813d66"}}}}