{"action":"created","comment":{"url":"https://api.github.com/repos/ncalexan/datomish/pulls/comments/83295452","pull_request_review_id":4094479,"id":83295452,"diff_hunk":"@@ -322,6 +322,221 @@\n (defn- transact-entity [report entity]\n   (update-in report [:entities] conj entity))\n \n+(defn id-literal-generation [unique-identity? entity]\n+  \"Group entities possibly containing id-literals into 'generations'.\n+\n+  Entities are grouped into one of the following generations:\n+\n+  :upserts-ev - 'complex upserts' that look like [:db/add -1 a -2] where a is :db.unique/identity;\n+\n+  :upserts-e - 'simple upserts' that look like [:db/add -1 a v] where a is :db.unique/identity;\n+\n+  :allocations-{e,v,ev} - things like [:db/add -1 b v], [:db/add e b -2], or [:db/add -3 b -4] where\n+  b is *not* :db.unique/identity, or like [:db/add e a -5] where a is :db.unique/identity;\n+\n+  :entities - not :db/add, or no id-literals.\"\n+  {:pre [(sequential? entity)]}\n+  (let [[op e a v] entity\n+        e?         (id-literal? e)\n+        a?         (id-literal? a)\n+        v?         (id-literal? v)]\n+    (cond\n+      (not= (first entity) :db/add) ;; TODO: verify no id-literals appear.\n+      :entities\n+\n+      a?\n+      (raise \"id-literal attributes are not yet supported: \" entity\n+             {:error  :transact/no-id-literal-attributes\n+              :entity entity })\n+\n+      e?\n+      (if (unique-identity? a)\n+        (if v?\n+          :upserts-ev\n+          :upserts-e)\n+        (if v?\n+          :allocations-ev\n+          :allocations-e))\n+\n+      v?\n+      :allocations-v\n+\n+      true\n+      :entities)))\n+\n+(defn <resolve-upserts-e [db upserts-e]\n+  \"Given a sequence of :upserts-e, query the database to try to map them to existing entities.\n+\n+  Returns a map of id-literals to integer entids, with keys only those id-literals that mapped to\n+  existing entities.\"\n+  (go-pair\n+    (if (seq upserts-e)\n+      (let [->id-av (fn [[op id-literal a v]] [id-literal [a v]])\n+            ;; Like {[id-literal [[:a1 :v1] [:a2 :v2] ...]] ...}.\n+            id->avs (util/group-by-kv ->id-av upserts-e)\n+            ;; Like [[:a1 :v1] [:a2 v2] ...].\n+            avs     (apply concat (vals id->avs))\n+            ;; Like {[[:a1 :v1] e1] ...}.\n+            av->e   (<? (db/<avs db avs))\n+\n+            avs->es (fn [avs] (set (keep (partial get av->e) avs)))\n+\n+            id->es  (util/mapvals avs->es id->avs)]\n+\n+        (into {}\n+              ;; nil is dropped.\n+              (map (fn [[id es]]\n+                     (when-let [e (first es)]\n+                       (when (second es)\n+                         (raise \"Conflicting upsert: \" id \" resolves\"\n+                                \" to more than one entid \" es\n+                                {:error :transact/upsert :tempid id :entids es}))\n+                       [id e])))\n+              id->es)))))\n+\n+(defn evolve-upserts-e [id->e upserts-e]\n+  (let [evolve1\n+        (fn [[op id-e a v]]\n+          (let [e* (get id->e id-e)]\n+            (if e*\n+              [:upserted [op e* a v]]\n+              [:allocations-e [op id-e a v]])))]\n+    (util/group-by-kv evolve1 upserts-e)))\n+\n+(defn evolve-upserts-ev [id->e upserts-ev]\n+  \"Given a map id->e of id-literals to integer entids, evolve the given entities.  Returns a map\n+  whose keys are generations and whose values are vectors of entities in those generations.\"\n+  (let [evolve1\n+        (fn [[op id-e a id-v]]\n+          (let [e* (get id->e id-e)\n+                v* (get id->e id-v)]\n+            (if e*\n+              (if v*\n+                [:resolved [op e* a v*]]\n+                [:allocations-v [op e* a id-v]])\n+              (if v*\n+                [:upserts-e [op id-e a v*]]\n+                [:upserts-ev [op id-e a id-v]]))))]\n+    (util/group-by-kv evolve1 upserts-ev)))\n+\n+(defn evolve-allocations-e [id->e allocations-e]\n+  \"Given a map id->e of id-literals to integer entids, evolve the given entities.  Returns a map\n+  whose keys are generations and whose values are vectors of entities in those generations.\"\n+  (let [evolve1\n+        (fn [[op id-e a v]]\n+          (let [e* (get id->e id-e)]\n+            (if e*\n+              [:resolved [op e* a v]]\n+              [:allocations-e [op id-e a v]])))]\n+    (util/group-by-kv evolve1 allocations-e)))\n+\n+(defn evolve-allocations-v [id->e allocations-v]\n+  \"Given a map id->e of id-literals to integer entids, evolve the given entities.  Returns a map\n+  whose keys are generations and whose values are vectors of entities in those generations.\"\n+  (let [evolve1\n+        (fn [[op e a id-v]]\n+          (let [v* (get id->e id-v)]\n+            (if v*\n+              [:resolved [op e a v*]]\n+              [:allocations-v [op e a id-v]])))]\n+    (util/group-by-kv evolve1 allocations-v)))\n+\n+(defn evolve-allocations-ev [id->e allocations-ev]\n+  \"Given a map id->e of id-literals to integer entids, evolve the entities in allocations-ev.  Returns a\n+  map whose keys are generations and whose values are vectors of entities in those generations.\"\n+  (let [evolve1\n+        (fn [[op id-e a id-v]]\n+          (let [e* (get id->e id-e)\n+                v* (get id->e id-v)]\n+            (if e*\n+              (if v*\n+                [:resolved [op e* a v*]]\n+                [:allocations-v [op e* a id-v]])\n+              (if v*\n+                [:allocations-e [op id-e a v*]]\n+                [:allocations-ev [op id-e a id-v]]))))]\n+    (util/group-by-kv evolve1 allocations-ev)))\n+\n+(defn <evolve [db evolution]\n+  \"Evolve a map of generations {:upserts-e [...], :upserts-ev [...], ...} as much as possible.\n+\n+  The algorithm is as follows.\n+\n+  First, resolve :upserts-e against the database.  Some [a v] -> e will upsert; some will not.\n+  Some :upserts-e evolve to become actual :upserts (they upserted!); any other :upserts-e evolve to\n+  become :allocations-e (they did not upsert, and will not upsert this transaction).\n+\n+  Using the newly upserted id-literals, some :upserts-ev evolve to become :resolved;\n+  some :upserts-ev evolve to become :upserts-e; and some :upserts-ev remain :upserts-ev.\n+\n+  Likewise, some :allocations-ev evolve to become :allocations-e, :allocations-v, or :resolved; some\n+  :allocations-e evolve to become :resolved; and some :allocations-v evolve to become :resolved.\n+\n+  If we have *new* :upserts-e (i.e., some :upserts-ev become :upserts-e), then we may be able to\n+  make more progress.  We recurse, trying to resolve these new :upserts-e.\n+\n+  Eventually we will have no :upserts-e.  At this point, :upserts-ev become :allocations-ev, and now\n+  we have :entities, :upserted, :resolved, and various :allocations-*.\n+\n+  As a future optimization, :upserts do not need to be inserted; they upserted, so they already\n+  exist in the DB.  (We still need to verify uniqueness and ensure no overlapping can occur.)\n+  Similary, :allocations-* do not need to be checked for existence, so they can be written to the DB\n+  faster.\"\n+  (go-pair\n+    (let [{:keys [upserted resolved upserts-ev upserts-e allocations-ev allocations-e allocations-v entities]} evolution]\n+      (if-not upserts-e\n+        ;; No more progress to be made.  Any upserts-ev must just be allocations.\n+        {:allocations-ev (concat upserts-ev allocations-ev)\n+         :allocations-e allocations-e\n+         :allocations-v allocations-v\n+         :upserted upserted\n+         :resolved resolved\n+         :entities entities}\n+        ;; Progress can be made.  Try to evolve further.\n+        (let [id->e (<? (<resolve-upserts-e db upserts-e))]\n+          (merge-with\n+            concat\n+            {:tempids  id->e ;; TODO: ensure we handle conflicting upserts across generations correctly here.","path":"src/common/datomish/transact.cljc","position":178,"original_position":178,"commit_id":"cc70595aa144c76f858f98910f5499b135c232e3","original_commit_id":"cc70595aa144c76f858f98910f5499b135c232e3","user":{"login":"rnewman","id":91722,"avatar_url":"https://avatars.githubusercontent.com/u/91722?v=3","gravatar_id":"","url":"https://api.github.com/users/rnewman","html_url":"https://github.com/rnewman","followers_url":"https://api.github.com/users/rnewman/followers","following_url":"https://api.github.com/users/rnewman/following{/other_user}","gists_url":"https://api.github.com/users/rnewman/gists{/gist_id}","starred_url":"https://api.github.com/users/rnewman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rnewman/subscriptions","organizations_url":"https://api.github.com/users/rnewman/orgs","repos_url":"https://api.github.com/users/rnewman/repos","events_url":"https://api.github.com/users/rnewman/events{/privacy}","received_events_url":"https://api.github.com/users/rnewman/received_events","type":"User","site_admin":false},"body":"Yeah, I was wondering this. What if in the first pass something's an `ev` waiting, and a `v` known? The unknown `v` gets resolved to an ID, but the `v` is known, so we push that tuple into `resolved`. In the next pass we now know the `v`, so we have an `upserts-e`. That resolves to a different entity, but we don't know there's a conflict because the other was already resolved.\r\n\r\nSo I think you need to keep `id->e` mappings around across generations.","created_at":"2016-10-13T19:41:40Z","updated_at":"2016-10-13T19:41:40Z","html_url":"https://github.com/ncalexan/datomish/pull/1#discussion_r83295452","pull_request_url":"https://api.github.com/repos/ncalexan/datomish/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/ncalexan/datomish/pulls/comments/83295452"},"html":{"href":"https://github.com/ncalexan/datomish/pull/1#discussion_r83295452"},"pull_request":{"href":"https://api.github.com/repos/ncalexan/datomish/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/ncalexan/datomish/pulls/1","id":89073334,"html_url":"https://github.com/ncalexan/datomish/pull/1","diff_url":"https://github.com/ncalexan/datomish/pull/1.diff","patch_url":"https://github.com/ncalexan/datomish/pull/1.patch","issue_url":"https://api.github.com/repos/ncalexan/datomish/issues/1","number":1,"state":"open","locked":false,"title":"Bulk id literals","user":{"login":"ncalexan","id":1097182,"avatar_url":"https://avatars.githubusercontent.com/u/1097182?v=3","gravatar_id":"","url":"https://api.github.com/users/ncalexan","html_url":"https://github.com/ncalexan","followers_url":"https://api.github.com/users/ncalexan/followers","following_url":"https://api.github.com/users/ncalexan/following{/other_user}","gists_url":"https://api.github.com/users/ncalexan/gists{/gist_id}","starred_url":"https://api.github.com/users/ncalexan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ncalexan/subscriptions","organizations_url":"https://api.github.com/users/ncalexan/orgs","repos_url":"https://api.github.com/users/ncalexan/repos","events_url":"https://api.github.com/users/ncalexan/events{/privacy}","received_events_url":"https://api.github.com/users/ncalexan/received_events","type":"User","site_admin":false},"body":"@rnewman I haven't written the _proof_ that this algorithm does the right thing, but it clearly terminates (!) and I think the formalism to show that it does the correct thing isn't particularly valuable.  I have sketched a correctness proof using strong induction on the number of generations required before an error is introduced, which I can try to fill in if you're unconvinced.\r\n\r\nThis is on top of the transact/listen work.","created_at":"2016-10-12T19:00:15Z","updated_at":"2016-10-13T19:48:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"505f59264c09944d3b5d8338f4438f7ebdc5583c","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/ncalexan/datomish/pulls/1/commits","review_comments_url":"https://api.github.com/repos/ncalexan/datomish/pulls/1/comments","review_comment_url":"https://api.github.com/repos/ncalexan/datomish/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ncalexan/datomish/issues/1/comments","statuses_url":"https://api.github.com/repos/ncalexan/datomish/statuses/cc70595aa144c76f858f98910f5499b135c232e3","head":{"label":"ncalexan:bulk-id-literals","ref":"bulk-id-literals","sha":"cc70595aa144c76f858f98910f5499b135c232e3","user":{"login":"ncalexan","id":1097182,"avatar_url":"https://avatars.githubusercontent.com/u/1097182?v=3","gravatar_id":"","url":"https://api.github.com/users/ncalexan","html_url":"https://github.com/ncalexan","followers_url":"https://api.github.com/users/ncalexan/followers","following_url":"https://api.github.com/users/ncalexan/following{/other_user}","gists_url":"https://api.github.com/users/ncalexan/gists{/gist_id}","starred_url":"https://api.github.com/users/ncalexan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ncalexan/subscriptions","organizations_url":"https://api.github.com/users/ncalexan/orgs","repos_url":"https://api.github.com/users/ncalexan/repos","events_url":"https://api.github.com/users/ncalexan/events{/privacy}","received_events_url":"https://api.github.com/users/ncalexan/received_events","type":"User","site_admin":false},"repo":{"id":70625837,"name":"datomish","full_name":"ncalexan/datomish","owner":{"login":"ncalexan","id":1097182,"avatar_url":"https://avatars.githubusercontent.com/u/1097182?v=3","gravatar_id":"","url":"https://api.github.com/users/ncalexan","html_url":"https://github.com/ncalexan","followers_url":"https://api.github.com/users/ncalexan/followers","following_url":"https://api.github.com/users/ncalexan/following{/other_user}","gists_url":"https://api.github.com/users/ncalexan/gists{/gist_id}","starred_url":"https://api.github.com/users/ncalexan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ncalexan/subscriptions","organizations_url":"https://api.github.com/users/ncalexan/orgs","repos_url":"https://api.github.com/users/ncalexan/repos","events_url":"https://api.github.com/users/ncalexan/events{/privacy}","received_events_url":"https://api.github.com/users/ncalexan/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ncalexan/datomish","description":"A persistent, relational store inspired by Datomic and DataScript.","fork":true,"url":"https://api.github.com/repos/ncalexan/datomish","forks_url":"https://api.github.com/repos/ncalexan/datomish/forks","keys_url":"https://api.github.com/repos/ncalexan/datomish/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ncalexan/datomish/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ncalexan/datomish/teams","hooks_url":"https://api.github.com/repos/ncalexan/datomish/hooks","issue_events_url":"https://api.github.com/repos/ncalexan/datomish/issues/events{/number}","events_url":"https://api.github.com/repos/ncalexan/datomish/events","assignees_url":"https://api.github.com/repos/ncalexan/datomish/assignees{/user}","branches_url":"https://api.github.com/repos/ncalexan/datomish/branches{/branch}","tags_url":"https://api.github.com/repos/ncalexan/datomish/tags","blobs_url":"https://api.github.com/repos/ncalexan/datomish/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ncalexan/datomish/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ncalexan/datomish/git/refs{/sha}","trees_url":"https://api.github.com/repos/ncalexan/datomish/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ncalexan/datomish/statuses/{sha}","languages_url":"https://api.github.com/repos/ncalexan/datomish/languages","stargazers_url":"https://api.github.com/repos/ncalexan/datomish/stargazers","contributors_url":"https://api.github.com/repos/ncalexan/datomish/contributors","subscribers_url":"https://api.github.com/repos/ncalexan/datomish/subscribers","subscription_url":"https://api.github.com/repos/ncalexan/datomish/subscription","commits_url":"https://api.github.com/repos/ncalexan/datomish/commits{/sha}","git_commits_url":"https://api.github.com/repos/ncalexan/datomish/git/commits{/sha}","comments_url":"https://api.github.com/repos/ncalexan/datomish/comments{/number}","issue_comment_url":"https://api.github.com/repos/ncalexan/datomish/issues/comments{/number}","contents_url":"https://api.github.com/repos/ncalexan/datomish/contents/{+path}","compare_url":"https://api.github.com/repos/ncalexan/datomish/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ncalexan/datomish/merges","archive_url":"https://api.github.com/repos/ncalexan/datomish/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ncalexan/datomish/downloads","issues_url":"https://api.github.com/repos/ncalexan/datomish/issues{/number}","pulls_url":"https://api.github.com/repos/ncalexan/datomish/pulls{/number}","milestones_url":"https://api.github.com/repos/ncalexan/datomish/milestones{/number}","notifications_url":"https://api.github.com/repos/ncalexan/datomish/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ncalexan/datomish/labels{/name}","releases_url":"https://api.github.com/repos/ncalexan/datomish/releases{/id}","deployments_url":"https://api.github.com/repos/ncalexan/datomish/deployments","created_at":"2016-10-11T18:58:08Z","updated_at":"2016-10-11T18:58:09Z","pushed_at":"2016-10-12T19:04:41Z","git_url":"git://github.com/ncalexan/datomish.git","ssh_url":"git@github.com:ncalexan/datomish.git","clone_url":"https://github.com/ncalexan/datomish.git","svn_url":"https://github.com/ncalexan/datomish","homepage":null,"size":417,"stargazers_count":0,"watchers_count":0,"language":"Clojure","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"ncalexan:listen-updated","ref":"listen-updated","sha":"a0b29ed5bfbd0925eb60f4aa3b14eff5152e615b","user":{"login":"ncalexan","id":1097182,"avatar_url":"https://avatars.githubusercontent.com/u/1097182?v=3","gravatar_id":"","url":"https://api.github.com/users/ncalexan","html_url":"https://github.com/ncalexan","followers_url":"https://api.github.com/users/ncalexan/followers","following_url":"https://api.github.com/users/ncalexan/following{/other_user}","gists_url":"https://api.github.com/users/ncalexan/gists{/gist_id}","starred_url":"https://api.github.com/users/ncalexan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ncalexan/subscriptions","organizations_url":"https://api.github.com/users/ncalexan/orgs","repos_url":"https://api.github.com/users/ncalexan/repos","events_url":"https://api.github.com/users/ncalexan/events{/privacy}","received_events_url":"https://api.github.com/users/ncalexan/received_events","type":"User","site_admin":false},"repo":{"id":70625837,"name":"datomish","full_name":"ncalexan/datomish","owner":{"login":"ncalexan","id":1097182,"avatar_url":"https://avatars.githubusercontent.com/u/1097182?v=3","gravatar_id":"","url":"https://api.github.com/users/ncalexan","html_url":"https://github.com/ncalexan","followers_url":"https://api.github.com/users/ncalexan/followers","following_url":"https://api.github.com/users/ncalexan/following{/other_user}","gists_url":"https://api.github.com/users/ncalexan/gists{/gist_id}","starred_url":"https://api.github.com/users/ncalexan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ncalexan/subscriptions","organizations_url":"https://api.github.com/users/ncalexan/orgs","repos_url":"https://api.github.com/users/ncalexan/repos","events_url":"https://api.github.com/users/ncalexan/events{/privacy}","received_events_url":"https://api.github.com/users/ncalexan/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ncalexan/datomish","description":"A persistent, relational store inspired by Datomic and DataScript.","fork":true,"url":"https://api.github.com/repos/ncalexan/datomish","forks_url":"https://api.github.com/repos/ncalexan/datomish/forks","keys_url":"https://api.github.com/repos/ncalexan/datomish/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ncalexan/datomish/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ncalexan/datomish/teams","hooks_url":"https://api.github.com/repos/ncalexan/datomish/hooks","issue_events_url":"https://api.github.com/repos/ncalexan/datomish/issues/events{/number}","events_url":"https://api.github.com/repos/ncalexan/datomish/events","assignees_url":"https://api.github.com/repos/ncalexan/datomish/assignees{/user}","branches_url":"https://api.github.com/repos/ncalexan/datomish/branches{/branch}","tags_url":"https://api.github.com/repos/ncalexan/datomish/tags","blobs_url":"https://api.github.com/repos/ncalexan/datomish/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ncalexan/datomish/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ncalexan/datomish/git/refs{/sha}","trees_url":"https://api.github.com/repos/ncalexan/datomish/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ncalexan/datomish/statuses/{sha}","languages_url":"https://api.github.com/repos/ncalexan/datomish/languages","stargazers_url":"https://api.github.com/repos/ncalexan/datomish/stargazers","contributors_url":"https://api.github.com/repos/ncalexan/datomish/contributors","subscribers_url":"https://api.github.com/repos/ncalexan/datomish/subscribers","subscription_url":"https://api.github.com/repos/ncalexan/datomish/subscription","commits_url":"https://api.github.com/repos/ncalexan/datomish/commits{/sha}","git_commits_url":"https://api.github.com/repos/ncalexan/datomish/git/commits{/sha}","comments_url":"https://api.github.com/repos/ncalexan/datomish/comments{/number}","issue_comment_url":"https://api.github.com/repos/ncalexan/datomish/issues/comments{/number}","contents_url":"https://api.github.com/repos/ncalexan/datomish/contents/{+path}","compare_url":"https://api.github.com/repos/ncalexan/datomish/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ncalexan/datomish/merges","archive_url":"https://api.github.com/repos/ncalexan/datomish/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ncalexan/datomish/downloads","issues_url":"https://api.github.com/repos/ncalexan/datomish/issues{/number}","pulls_url":"https://api.github.com/repos/ncalexan/datomish/pulls{/number}","milestones_url":"https://api.github.com/repos/ncalexan/datomish/milestones{/number}","notifications_url":"https://api.github.com/repos/ncalexan/datomish/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ncalexan/datomish/labels{/name}","releases_url":"https://api.github.com/repos/ncalexan/datomish/releases{/id}","deployments_url":"https://api.github.com/repos/ncalexan/datomish/deployments","created_at":"2016-10-11T18:58:08Z","updated_at":"2016-10-11T18:58:09Z","pushed_at":"2016-10-12T19:04:41Z","git_url":"git://github.com/ncalexan/datomish.git","ssh_url":"git@github.com:ncalexan/datomish.git","clone_url":"https://github.com/ncalexan/datomish.git","svn_url":"https://github.com/ncalexan/datomish","homepage":null,"size":417,"stargazers_count":0,"watchers_count":0,"language":"Clojure","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ncalexan/datomish/pulls/1"},"html":{"href":"https://github.com/ncalexan/datomish/pull/1"},"issue":{"href":"https://api.github.com/repos/ncalexan/datomish/issues/1"},"comments":{"href":"https://api.github.com/repos/ncalexan/datomish/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/ncalexan/datomish/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/ncalexan/datomish/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ncalexan/datomish/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/ncalexan/datomish/statuses/cc70595aa144c76f858f98910f5499b135c232e3"}}}}
{"actor":{"display_login":"rnewman"}}