payload: {"action":"created","comment":{"url":"https://api.github.com/repos/cylc/cylc-admin/pulls/comments/361046878","pull_request_review_id":336062347,"id":361046878,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDM2MTA0Njg3OA==","diff_hunk":"@@ -0,0 +1,280 @@\n+# Subscription Model Proposal\n+\n+## The Introduction\n+\n+Two of the central pillars of the new UI are incremental updates and fine-grained subscriptions.\n+\n+* Incremental updates involves sending only the data which has changed to the UI, this:\n+    * Reduces network traffic.\n+    * Reduces the memory footprint of the UI.\n+    * Allows us to move away from using a global update loop which makes the UI more responsive.\n+* Fine grained subscriptions allow individual Vues (Tree, Graph, Dot, ...) to subscribe to the set o data which they actually need at a particular moment in time, this;\n+    * Reduces network traffic.\n+    * Reduces the memory footprint of the UI.\n+    * Reduces the number of items the UI has to process making updates faster.\n+    * Enables the UIServer to dynamically adjust its subscriptions to the WS so as to keep load on the workflow to a minimum.\n+\n+By reducing the about of data we send to the UI we reduce the load on the browser enabling us to do more with the UI and to make it more responsive.\n+\n+## The Proposal\n+\n+1. Individual Vues register and update their subscriptions with a service in the UI *(done)*.\n+2. This service \"merges\" subscriptions together then registers the result with the UIS *(done)*.\n+3. The UIS parses and stores these subscriptions.\n+4. When deltas arrive from the WS, the UIS loops through active subscriptions to see if there is an overlap.\n+   * The \"overlap\" is the intersection of the delta and subscription fields.\n+   * This is event driven as we only need to do it as and when deltas arrive.\n+5. If there is an overlap send these fields to the UI which registered the subscription as a delta.\n+\n+## The Detail\n+\n+### The Three Data Stores\n+\n+We have three data stores, at the WS, the UIS and the UI:\n+\n+* The Workflow Servers (WS) each have their own data stores.\n+* The UIS has one data store containing the relevant subset of multiple Workflow Servers' data stores.\n+* The UI has one data store containing the requested subset of the UIS data store.\n+\n+All three represent exactly the same data model. The latter two must be kept in-sync with the former.\n+\n+* The UIS holds a subset of the data from the Workflow Servers.\n+* The UI holds a subset of the data from the UIS.\n+\n+### The Ideal Subscription Interface\n+\n+A subscription look something like this:\n+\n+```\n+subscription {\n+  a {\n+    b\n+    c { \n+      d\n+      e\n+    }\n+    f\n+}\n+```\n+\n+Deltas look something like this:\n+\n+```\n+{\n+  b: 1,\n+}\n+\n+```\n+\n+```\n+{\n+  b: 1,\n+  c {\n+    d: 2\n+  }\n+}\n+```\n+\n+A subscription is a `set`, a delta is a `dict` which is a subset of the subscription.\n+\n+### The UI\n+\n+#### Similar But Different Subscriptions (1)\n+\n+The UI consists of different Vues which have their own subscriptions.\n+\n+For example a simple Tree Vue might have the following subscription:\n+\n+```\n+subscription {\n+  workflows(ids: [<id>]) {\n+    taskProxies {\n+      name\n+      status\n+      firstParent {\n+        id\n+      }\n+    }\n+    familyProxies {\n+      id\n+      name\n+      firstParent {\n+      }\n+    }\n+  }\n+}\n+```\n+\n+Whereas an un-grouped Graph Vue might have this subscription:\n+\n+```\n+subscription {\n+  workflows(ids: [<id>]) {\n+    taskProxies {\n+      name\n+      id\n+      status\n+    }\n+    nodesEdges {\n+      nodes {\n+        id\n+      }\n+      edges {\n+        source\n+        target\n+      }\n+    }\n+  }\n+}\n+```\n+\n+These subscriptions are different but they have a large overlap. If we were to register both subscriptions with the UIS we would end up with two similar but different data stores in the UI. This would be in-efficient and opens up discontinuity issues.\n+\n+Users may want to open many similar but different views on the same workflow so we need to be able to handle this in an elegant manner.\n+\n+#### Merging Subscriptions (2)\n+\n+One solution to this problem is to \"merge\" subscriptions together.\n+\n+Subscriptions are effectively \"sets\" of data fields, two subscriptions can be \"merged\" by taking their union. For example the union of the previous two subscriptions is this:\n+\n+```\n+subscription {\n+  workflows(ids: [<id>]) {\n+    taskProxies {\n+      name\n+      id\n+      status\n+      firstParent {\n+        id\n+      }\n+    }\n+    familyProxies {\n+      id\n+      name\n+      firstParent {\n+      }\n+    }\n+    nodesEdges {\n+      nodes {\n+        id\n+      }\n+      edges {\n+        source\n+        target\n+      }\n+    }\n+  }\n+}\n+```\n+\n+As an added bonus we can query the UI to find out exactly what it is subscribing to and exactly which view has asked for what data making optimisation/debugging much nicer.\n+\n+### The Workflow Server\n+\n+When events occur the WS sends \"deltas\" containing only the changed information.\n+\n+For example if a job has changed state from submitted to running we might expect a delta of the form:\n+\n+```\n+taskProxies {\n+  id: <id>,\n+  status: \"running\"\n+  jobs {\n+    id: <id>,\n+    status: \"running\"\n+  }\n+}\n+```\n+\n+### The UI Server\n+\n+#### Subscriptions (3)\n+\n+When subscriptions are registered with the UIS they are stored locally in a mapping so we can associate subscriptions with open websockets.\n+\n+#### Deltas (4)\n+\n+When deltas arrive from the WS we loop through the subscriptions and take the intersection of the delta and subscription fields, if there is an overlap, this is the delta we send to the UI.\n+\n+#### Overlaps (5)\n+\n+For example using the previous delta and subscription:\n+\n+\n+<table>\n+  <th>\n+    <td>Delta</td>\n+    <td>Subscription</td>\n+    <td>Intersection</td>\n+  </th>\n+  <tr>\n+    <td>\n+        Example 1\n+    </td>\n+    <td>\n+      <pre>\n+workflows(ids: [<id>]) {\n+  taskProxies {\n+    id: <id>,\n+    status: \"running\"\n+    jobs {\n+      id: <id>,\n+      status: \"running\"\n+    }\n+  }\n+}\n+      </pre>\n+    </td>\n+    <td>\n+      <pre>\n+subscription {\n+  workflows(ids: [<id>]) {\n+    taskProxies {\n+      name\n+      id\n+      status\n+      firstParent {\n+        id\n+      }\n+    }\n+    familyProxies {\n+      id\n+      name\n+      firstParent {\n+      }\n+    }\n+    nodesEdges {\n+      nodes {\n+        id\n+      }\n+      edges {\n+        source\n+        target\n+      }\n+    }\n+  }\n+}\n+      </pre>\n+    </td>\n+    <td>\n+      <pre>\n+workflows(ids: [<id>]) {\n+  taskProxies {\n+    id: <id>,\n+    status: \"running\"\n+  }\n+}\n+      </pre>\n+    </td>\n+  </tr>\n+</table>\n+\n+## Caveats\n+\n+* The comparison of delta and subscription fields will need to be fast.\n+  * Else the UIS will become CPU heavy and sluggish.\n+  * There may be potential for caching results.\n+* GraphQL interface does not provide a mechanism for deltas out of the box.\n+  * We may have to extend / implement this ourselves (can we add a `deltaSubscribe` type to GraphQL?).\n+  * If we did change the behaviour of the `subscribe` type 3rd party tools (incl graphiql) would loose the ability to work subscriptions (probably not a problem but worth noting).","path":"docs/proposal-subscriptions.md","position":280,"original_position":280,"commit_id":"ee6b3e8053f10d116fd650a46c991cb6a93f58d1","original_commit_id":"ee6b3e8053f10d116fd650a46c991cb6a93f58d1","user":{"login":"oliver-sanders","id":16705946,"node_id":"MDQ6VXNlcjE2NzA1OTQ2","avatar_url":"https://avatars0.githubusercontent.com/u/16705946?v=4","gravatar_id":"","url":"https://api.github.com/users/oliver-sanders","html_url":"https://github.com/oliver-sanders","followers_url":"https://api.github.com/users/oliver-sanders/followers","following_url":"https://api.github.com/users/oliver-sanders/following{/other_user}","gists_url":"https://api.github.com/users/oliver-sanders/gists{/gist_id}","starred_url":"https://api.github.com/users/oliver-sanders/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliver-sanders/subscriptions","organizations_url":"https://api.github.com/users/oliver-sanders/orgs","repos_url":"https://api.github.com/users/oliver-sanders/repos","events_url":"https://api.github.com/users/oliver-sanders/events{/privacy}","received_events_url":"https://api.github.com/users/oliver-sanders/received_events","type":"User","site_admin":false},"body":"Yes, this is the part we would need to solve ourselves.","created_at":"2019-12-24T02:27:32Z","updated_at":"2019-12-24T02:27:33Z","html_url":"https://github.com/cylc/cylc-admin/pull/73#discussion_r361046878","pull_request_url":"https://api.github.com/repos/cylc/cylc-admin/pulls/73","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/cylc/cylc-admin/pulls/comments/361046878"},"html":{"href":"https://github.com/cylc/cylc-admin/pull/73#discussion_r361046878"},"pull_request":{"href":"https://api.github.com/repos/cylc/cylc-admin/pulls/73"}},"in_reply_to_id":361042398},"pull_request":{"url":"https://api.github.com/repos/cylc/cylc-admin/pulls/73","id":356472965,"node_id":"MDExOlB1bGxSZXF1ZXN0MzU2NDcyOTY1","html_url":"https://github.com/cylc/cylc-admin/pull/73","diff_url":"https://github.com/cylc/cylc-admin/pull/73.diff","patch_url":"https://github.com/cylc/cylc-admin/pull/73.patch","issue_url":"https://api.github.com/repos/cylc/cylc-admin/issues/73","number":73,"state":"open","locked":false,"title":"proposal: subscription model","user":{"login":"oliver-sanders","id":16705946,"node_id":"MDQ6VXNlcjE2NzA1OTQ2","avatar_url":"https://avatars0.githubusercontent.com/u/16705946?v=4","gravatar_id":"","url":"https://api.github.com/users/oliver-sanders","html_url":"https://github.com/oliver-sanders","followers_url":"https://api.github.com/users/oliver-sanders/followers","following_url":"https://api.github.com/users/oliver-sanders/following{/other_user}","gists_url":"https://api.github.com/users/oliver-sanders/gists{/gist_id}","starred_url":"https://api.github.com/users/oliver-sanders/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliver-sanders/subscriptions","organizations_url":"https://api.github.com/users/oliver-sanders/orgs","repos_url":"https://api.github.com/users/oliver-sanders/repos","events_url":"https://api.github.com/users/oliver-sanders/events{/privacy}","received_events_url":"https://api.github.com/users/oliver-sanders/received_events","type":"User","site_admin":false},"body":"Proposal for how subscriptions could be handled at the UIS.\r\n\r\nThis is effectively a written form of the following diagram:\r\n\r\n![graphql](https://user-images.githubusercontent.com/16705946/71384241-285cf100-2645-11ea-90bc-418725a9966b.png)\r\n\r\nBut with subscription examples which are loosely based on reality.\r\n\r\nPlease comment, feel free to raise larger changes as pull requests against this branch.","created_at":"2019-12-23T23:02:59Z","updated_at":"2019-12-24T02:27:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d8cb04b1147bcc49b2392f351bf65e433c9c780f","assignee":{"login":"oliver-sanders","id":16705946,"node_id":"MDQ6VXNlcjE2NzA1OTQ2","avatar_url":"https://avatars0.githubusercontent.com/u/16705946?v=4","gravatar_id":"","url":"https://api.github.com/users/oliver-sanders","html_url":"https://github.com/oliver-sanders","followers_url":"https://api.github.com/users/oliver-sanders/followers","following_url":"https://api.github.com/users/oliver-sanders/following{/other_user}","gists_url":"https://api.github.com/users/oliver-sanders/gists{/gist_id}","starred_url":"https://api.github.com/users/oliver-sanders/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliver-sanders/subscriptions","organizations_url":"https://api.github.com/users/oliver-sanders/orgs","repos_url":"https://api.github.com/users/oliver-sanders/repos","events_url":"https://api.github.com/users/oliver-sanders/events{/privacy}","received_events_url":"https://api.github.com/users/oliver-sanders/received_events","type":"User","site_admin":false},"assignees":[{"login":"oliver-sanders","id":16705946,"node_id":"MDQ6VXNlcjE2NzA1OTQ2","avatar_url":"https://avatars0.githubusercontent.com/u/16705946?v=4","gravatar_id":"","url":"https://api.github.com/users/oliver-sanders","html_url":"https://github.com/oliver-sanders","followers_url":"https://api.github.com/users/oliver-sanders/followers","following_url":"https://api.github.com/users/oliver-sanders/following{/other_user}","gists_url":"https://api.github.com/users/oliver-sanders/gists{/gist_id}","starred_url":"https://api.github.com/users/oliver-sanders/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliver-sanders/subscriptions","organizations_url":"https://api.github.com/users/oliver-sanders/orgs","repos_url":"https://api.github.com/users/oliver-sanders/repos","events_url":"https://api.github.com/users/oliver-sanders/events{/privacy}","received_events_url":"https://api.github.com/users/oliver-sanders/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/cylc/cylc-admin/pulls/73/commits","review_comments_url":"https://api.github.com/repos/cylc/cylc-admin/pulls/73/comments","review_comment_url":"https://api.github.com/repos/cylc/cylc-admin/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cylc/cylc-admin/issues/73/comments","statuses_url":"https://api.github.com/repos/cylc/cylc-admin/statuses/ee6b3e8053f10d116fd650a46c991cb6a93f58d1","head":{"label":"oliver-sanders:subscription-model","ref":"subscription-model","sha":"ee6b3e8053f10d116fd650a46c991cb6a93f58d1","user":{"login":"oliver-sanders","id":16705946,"node_id":"MDQ6VXNlcjE2NzA1OTQ2","avatar_url":"https://avatars0.githubusercontent.com/u/16705946?v=4","gravatar_id":"","url":"https://api.github.com/users/oliver-sanders","html_url":"https://github.com/oliver-sanders","followers_url":"https://api.github.com/users/oliver-sanders/followers","following_url":"https://api.github.com/users/oliver-sanders/following{/other_user}","gists_url":"https://api.github.com/users/oliver-sanders/gists{/gist_id}","starred_url":"https://api.github.com/users/oliver-sanders/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliver-sanders/subscriptions","organizations_url":"https://api.github.com/users/oliver-sanders/orgs","repos_url":"https://api.github.com/users/oliver-sanders/repos","events_url":"https://api.github.com/users/oliver-sanders/events{/privacy}","received_events_url":"https://api.github.com/users/oliver-sanders/received_events","type":"User","site_admin":false},"repo":{"id":156854139,"node_id":"MDEwOlJlcG9zaXRvcnkxNTY4NTQxMzk=","name":"cylc-admin","full_name":"oliver-sanders/cylc-admin","private":false,"owner":{"login":"oliver-sanders","id":16705946,"node_id":"MDQ6VXNlcjE2NzA1OTQ2","avatar_url":"https://avatars0.githubusercontent.com/u/16705946?v=4","gravatar_id":"","url":"https://api.github.com/users/oliver-sanders","html_url":"https://github.com/oliver-sanders","followers_url":"https://api.github.com/users/oliver-sanders/followers","following_url":"https://api.github.com/users/oliver-sanders/following{/other_user}","gists_url":"https://api.github.com/users/oliver-sanders/gists{/gist_id}","starred_url":"https://api.github.com/users/oliver-sanders/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliver-sanders/subscriptions","organizations_url":"https://api.github.com/users/oliver-sanders/orgs","repos_url":"https://api.github.com/users/oliver-sanders/repos","events_url":"https://api.github.com/users/oliver-sanders/events{/privacy}","received_events_url":"https://api.github.com/users/oliver-sanders/received_events","type":"User","site_admin":false},"html_url":"https://github.com/oliver-sanders/cylc-admin","description":"Project planning documents etc. for the Cylc Workflow Engine - https://github.com/cylc/cylc","fork":true,"url":"https://api.github.com/repos/oliver-sanders/cylc-admin","forks_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/forks","keys_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/teams","hooks_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/hooks","issue_events_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/issues/events{/number}","events_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/events","assignees_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/assignees{/user}","branches_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/branches{/branch}","tags_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/tags","blobs_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/git/refs{/sha}","trees_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/statuses/{sha}","languages_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/languages","stargazers_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/stargazers","contributors_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/contributors","subscribers_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/subscribers","subscription_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/subscription","commits_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/commits{/sha}","git_commits_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/git/commits{/sha}","comments_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/comments{/number}","issue_comment_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/issues/comments{/number}","contents_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/contents/{+path}","compare_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/merges","archive_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/downloads","issues_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/issues{/number}","pulls_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/pulls{/number}","milestones_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/milestones{/number}","notifications_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/labels{/name}","releases_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/releases{/id}","deployments_url":"https://api.github.com/repos/oliver-sanders/cylc-admin/deployments","created_at":"2018-11-09T11:38:43Z","updated_at":"2019-12-09T02:47:01Z","pushed_at":"2019-12-23T23:00:45Z","git_url":"git://github.com/oliver-sanders/cylc-admin.git","ssh_url":"git@github.com:oliver-sanders/cylc-admin.git","clone_url":"https://github.com/oliver-sanders/cylc-admin.git","svn_url":"https://github.com/oliver-sanders/cylc-admin","homepage":"","size":3342,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"cylc:master","ref":"master","sha":"0951ada9032309efebb8ef0b468100cb428c52cb","user":{"login":"cylc","id":824878,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgyNDg3OA==","avatar_url":"https://avatars1.githubusercontent.com/u/824878?v=4","gravatar_id":"","url":"https://api.github.com/users/cylc","html_url":"https://github.com/cylc","followers_url":"https://api.github.com/users/cylc/followers","following_url":"https://api.github.com/users/cylc/following{/other_user}","gists_url":"https://api.github.com/users/cylc/gists{/gist_id}","starred_url":"https://api.github.com/users/cylc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cylc/subscriptions","organizations_url":"https://api.github.com/users/cylc/orgs","repos_url":"https://api.github.com/users/cylc/repos","events_url":"https://api.github.com/users/cylc/events{/privacy}","received_events_url":"https://api.github.com/users/cylc/received_events","type":"Organization","site_admin":false},"repo":{"id":155626083,"node_id":"MDEwOlJlcG9zaXRvcnkxNTU2MjYwODM=","name":"cylc-admin","full_name":"cylc/cylc-admin","private":false,"owner":{"login":"cylc","id":824878,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgyNDg3OA==","avatar_url":"https://avatars1.githubusercontent.com/u/824878?v=4","gravatar_id":"","url":"https://api.github.com/users/cylc","html_url":"https://github.com/cylc","followers_url":"https://api.github.com/users/cylc/followers","following_url":"https://api.github.com/users/cylc/following{/other_user}","gists_url":"https://api.github.com/users/cylc/gists{/gist_id}","starred_url":"https://api.github.com/users/cylc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cylc/subscriptions","organizations_url":"https://api.github.com/users/cylc/orgs","repos_url":"https://api.github.com/users/cylc/repos","events_url":"https://api.github.com/users/cylc/events{/privacy}","received_events_url":"https://api.github.com/users/cylc/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/cylc/cylc-admin","description":"Project planning for the Cylc Workflow Engine.","fork":false,"url":"https://api.github.com/repos/cylc/cylc-admin","forks_url":"https://api.github.com/repos/cylc/cylc-admin/forks","keys_url":"https://api.github.com/repos/cylc/cylc-admin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cylc/cylc-admin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cylc/cylc-admin/teams","hooks_url":"https://api.github.com/repos/cylc/cylc-admin/hooks","issue_events_url":"https://api.github.com/repos/cylc/cylc-admin/issues/events{/number}","events_url":"https://api.github.com/repos/cylc/cylc-admin/events","assignees_url":"https://api.github.com/repos/cylc/cylc-admin/assignees{/user}","branches_url":"https://api.github.com/repos/cylc/cylc-admin/branches{/branch}","tags_url":"https://api.github.com/repos/cylc/cylc-admin/tags","blobs_url":"https://api.github.com/repos/cylc/cylc-admin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cylc/cylc-admin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cylc/cylc-admin/git/refs{/sha}","trees_url":"https://api.github.com/repos/cylc/cylc-admin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cylc/cylc-admin/statuses/{sha}","languages_url":"https://api.github.com/repos/cylc/cylc-admin/languages","stargazers_url":"https://api.github.com/repos/cylc/cylc-admin/stargazers","contributors_url":"https://api.github.com/repos/cylc/cylc-admin/contributors","subscribers_url":"https://api.github.com/repos/cylc/cylc-admin/subscribers","subscription_url":"https://api.github.com/repos/cylc/cylc-admin/subscription","commits_url":"https://api.github.com/repos/cylc/cylc-admin/commits{/sha}","git_commits_url":"https://api.github.com/repos/cylc/cylc-admin/git/commits{/sha}","comments_url":"https://api.github.com/repos/cylc/cylc-admin/comments{/number}","issue_comment_url":"https://api.github.com/repos/cylc/cylc-admin/issues/comments{/number}","contents_url":"https://api.github.com/repos/cylc/cylc-admin/contents/{+path}","compare_url":"https://api.github.com/repos/cylc/cylc-admin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cylc/cylc-admin/merges","archive_url":"https://api.github.com/repos/cylc/cylc-admin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cylc/cylc-admin/downloads","issues_url":"https://api.github.com/repos/cylc/cylc-admin/issues{/number}","pulls_url":"https://api.github.com/repos/cylc/cylc-admin/pulls{/number}","milestones_url":"https://api.github.com/repos/cylc/cylc-admin/milestones{/number}","notifications_url":"https://api.github.com/repos/cylc/cylc-admin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cylc/cylc-admin/labels{/name}","releases_url":"https://api.github.com/repos/cylc/cylc-admin/releases{/id}","deployments_url":"https://api.github.com/repos/cylc/cylc-admin/deployments","created_at":"2018-10-31T21:31:59Z","updated_at":"2019-12-18T00:58:32Z","pushed_at":"2019-12-23T23:03:00Z","git_url":"git://github.com/cylc/cylc-admin.git","ssh_url":"git@github.com:cylc/cylc-admin.git","clone_url":"https://github.com/cylc/cylc-admin.git","svn_url":"https://github.com/cylc/cylc-admin","homepage":"https://cylc.github.io/cylc-admin/","size":3345,"stargazers_count":4,"watchers_count":4,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":9,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":15,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"forks":9,"open_issues":15,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cylc/cylc-admin/pulls/73"},"html":{"href":"https://github.com/cylc/cylc-admin/pull/73"},"issue":{"href":"https://api.github.com/repos/cylc/cylc-admin/issues/73"},"comments":{"href":"https://api.github.com/repos/cylc/cylc-admin/issues/73/comments"},"review_comments":{"href":"https://api.github.com/repos/cylc/cylc-admin/pulls/73/comments"},"review_comment":{"href":"https://api.github.com/repos/cylc/cylc-admin/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cylc/cylc-admin/pulls/73/commits"},"statuses":{"href":"https://api.github.com/repos/cylc/cylc-admin/statuses/ee6b3e8053f10d116fd650a46c991cb6a93f58d1"}},"author_association":"MEMBER"}}
other: {"actor":{"display_login":"oliver-sanders"}}