From 30600004e42319b8e922ad67772f53cd0fb06515 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dandelion=20Man=C3=A9?=
Date: Mon, 19 Mar 2018 12:26:32 -0700
Subject: [PATCH] Implement the GitHub graph parser (#81)
The GitHub parser transforms GraphQL api data from GitHub into our Graph
data structure. This commit focuses on properly parsing Issues, Pull
Requests, Comments, and Users.
Test Plan:
Run the unit tests. Inspect the snapshot results (particuarly those for
individual pull requests or issues, which are easier to parse) and
verify that the output is appropriate.
---
.../__snapshots__/githubPlugin.test.js.snap | 536 ++++++++++++++++++
src/backend/githubPlugin.js | 244 +++++++-
src/backend/githubPlugin.test.js | 84 +++
3 files changed, 850 insertions(+), 14 deletions(-)
create mode 100644 src/backend/__snapshots__/githubPlugin.test.js.snap
create mode 100644 src/backend/githubPlugin.test.js
diff --git a/src/backend/__snapshots__/githubPlugin.test.js.snap b/src/backend/__snapshots__/githubPlugin.test.js.snap
new file mode 100644
index 0000000..402e77f
--- /dev/null
+++ b/src/backend/__snapshots__/githubPlugin.test.js.snap
@@ -0,0 +1,536 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`GithubParser issue parsing parses a simple issue (https://github.com/sourcecred/example-repo/issues/1) 1`] = `
+Object {
+ "edges": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ4MTg=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDA5MzQ4MTg=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ },
+ "nodes": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "login": "dandelionmane",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ4MTg=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "This is just an example issue.",
+ "number": 1,
+ "title": "An example issue.",
+ },
+ },
+ },
+}
+`;
+
+exports[`GithubParser issue parsing parses an issue with comments (https://github.com/sourcecred/example-repo/issues/6) 1`] = `
+Object {
+ "edges": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDU5OTM3NzM=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDU5OTM3NzM=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDU5OTM3NzM=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ },
+ "nodes": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "A wild COMMENT appeared!",
+ "url": "https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768442",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "And the maintainer said, \\"Let there be comments!\\"",
+ "url": "https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768538",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "login": "dandelionmane",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "This issue shall shortly have a few comments.",
+ "number": 6,
+ "title": "An issue with comments",
+ },
+ },
+ },
+}
+`;
+
+exports[`GithubParser pull request parsing parses a pr with review comments (https://github.com/sourcecred/example-repo/pull/3) 1`] = `
+Object {
+ "edges": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy\\",\\"type\\":\\"PULL_REQUEST\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ },
+ "nodes": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "@wchargin could you please do the following:
+- add a commit comment
+- add a review comment requesting some trivial change
+- i'll change it
+- then approve the pr",
+ "number": 5,
+ "title": "This pull request will be more contentious. I can feel it...",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "login": "dandelionmane",
+ },
+ },
+ },
+}
+`;
+
+exports[`GithubParser pull request parsing parses a simple pull request (https://github.com/sourcecred/example-repo/pull/3) 1`] = `
+Object {
+ "edges": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\",\\"type\\":\\"PULL_REQUEST\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\",\\"type\\":\\"PULL_REQUEST\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ },
+ "nodes": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "Oh look, it's a pull request.",
+ "number": 3,
+ "title": "Add README, merge via PR.",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "It seems apropos to reference something from a pull request comment... eg: #2 ",
+ "url": "https://github.com/sourcecred/example-repo/pull/3#issuecomment-369162222",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "login": "dandelionmane",
+ },
+ },
+ },
+}
+`;
+
+exports[`GithubParser whole repo parsing parses the entire example-repo as expected 1`] = `
+Object {
+ "edges": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\",\\"type\\":\\"PULL_REQUEST\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy\\",\\"type\\":\\"PULL_REQUEST\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ4MTg=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDA5MzQ4MTg=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzYzNzQ=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDA5MzYzNzQ=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"authorID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"},\\\\\\"contributionID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"AUTHORSHIP\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDQ6VXNlcjE0MDAwMjM=\\",\\"type\\":\\"USER\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDU5OTM3NzM=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\",\\"type\\":\\"PULL_REQUEST\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDU5OTM3NzM=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDU5OTM3NzM=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"childID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"},\\\\\\"parentID\\\\\\":{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"},\\\\\\"type\\\\\\":\\\\\\"CONTAINMENT\\\\\\"}\\"}": Object {
+ "dst": Object {
+ "id": "{\\"id\\":\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==\\",\\"type\\":\\"COMMENT\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ "payload": Object {},
+ "src": Object {
+ "id": "{\\"id\\":\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\",\\"type\\":\\"ISSUE\\"}",
+ "pluginName": "sourcecred/github-beta",
+ "repositoryName": "sourcecred/example-repo",
+ },
+ },
+ },
+ "nodes": Object {
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "Oh look, it's a pull request.",
+ "number": 3,
+ "title": "Add README, merge via PR.",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy\\\\\\",\\\\\\"type\\\\\\":\\\\\\"PULL_REQUEST\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "@wchargin could you please do the following:
+- add a commit comment
+- add a review comment requesting some trivial change
+- i'll change it
+- then approve the pr",
+ "number": 5,
+ "title": "This pull request will be more contentious. I can feel it...",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "It seems apropos to reference something from a pull request comment... eg: #2 ",
+ "url": "https://github.com/sourcecred/example-repo/pull/3#issuecomment-369162222",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "A wild COMMENT appeared!",
+ "url": "https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768442",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "And the maintainer said, \\"Let there be comments!\\"",
+ "url": "https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768538",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "It should also be possible to reference by exact url: https://github.com/sourcecred/example-repo/issues/6",
+ "url": "https://github.com/sourcecred/example-repo/issues/2#issuecomment-373768703",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==\\\\\\",\\\\\\"type\\\\\\":\\\\\\"COMMENT\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "We might also reference individual comments directly.
+https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768538",
+ "url": "https://github.com/sourcecred/example-repo/issues/2#issuecomment-373768850",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDQ6VXNlcjE0MDAwMjM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"USER\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "login": "dandelionmane",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ4MTg=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "This is just an example issue.",
+ "number": 1,
+ "title": "An example issue.",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzQ5ODA=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "This issue references another issue, namely #1",
+ "number": 2,
+ "title": "A referencing issue.",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDA5MzYzNzQ=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "Alas, its life as an open issue had only just begun.",
+ "number": 4,
+ "title": "A closed pull request",
+ },
+ },
+ "{\\"pluginName\\":\\"sourcecred/github-beta\\",\\"repositoryName\\":\\"sourcecred/example-repo\\",\\"id\\":\\"{\\\\\\"id\\\\\\":\\\\\\"MDU6SXNzdWUzMDU5OTM3NzM=\\\\\\",\\\\\\"type\\\\\\":\\\\\\"ISSUE\\\\\\"}\\"}": Object {
+ "payload": Object {
+ "body": "This issue shall shortly have a few comments.",
+ "number": 6,
+ "title": "An issue with comments",
+ },
+ },
+ },
+}
+`;
diff --git a/src/backend/githubPlugin.js b/src/backend/githubPlugin.js
index eaf00be..ad78aba 100644
--- a/src/backend/githubPlugin.js
+++ b/src/backend/githubPlugin.js
@@ -1,43 +1,259 @@
// @flow
+import type {Node, Edge} from "./graph";
+import type {Address} from "./address";
+import {Graph} from "./graph";
+const stringify = require("json-stable-stringify");
+
export const GITHUB_PLUGIN_NAME = "sourcecred/github-beta";
export type IssueNodePayload = {|
- +type: "ISSUE",
+title: string,
+number: number,
+ +body: string,
+|};
+export type IssueNodeID = {|
+ +type: "ISSUE",
+ +id: string,
|};
export type PullRequestNodePayload = {|
- +type: "PULL_REQUEST",
+title: string,
+number: number,
+ +body: string,
+|};
+export type PullRequestNodeID = {|
+ +type: "PULL_REQUEST",
+ +id: string,
|};
export type CommentNodePayload = {|
- +type: "COMMENT",
+ +url: string,
+body: string,
|};
+export type CommentNodeID = {|
+ +type: "COMMENT",
+ +id: string,
+|};
export type UserNodePayload = {|
- +type: "USER",
- +userName: string,
+ +login: string,
|};
+export type UserNodeID = {|
+ +type: "USER",
+ +id: string,
+|};
+
+export type BotNodePayload = {|
+ +login: string,
+|};
+export type BotNodeID = {|
+ +type: "BOT",
+ +id: string,
+|};
+
+export type OrganizationNodePayload = {|
+ +login: string,
+|};
+export type OrganizationNodeID = {|
+ +type: "ORGANIZATION",
+ +id: string,
+|};
+
+export type AuthorNodePayload =
+ | UserNodePayload
+ | BotNodePayload
+ | OrganizationNodePayload;
+export type AuthorNodeID = UserNodeID | BotNodeID | OrganizationNodeID;
export type NodePayload =
| IssueNodePayload
| PullRequestNodePayload
| CommentNodePayload
- | UserNodePayload;
-export type NodeType = $ElementType;
+ | AuthorNodePayload;
+export type NodeID =
+ | IssueNodeID
+ | PullRequestNodeID
+ | CommentNodeID
+ | AuthorNodeID;
+export type NodeType = $ElementType;
-export type AuthorshipEdgePayload = {|
+export type AuthorshipEdgePayload = {};
+export type AuthorshipEdgeID = {
+type: "AUTHORSHIP",
-|};
-
-export type ReferenceEdgePayload = {|
+ +contributionID: NodeID,
+ +authorID: NodeID,
+};
+export type ContainmentEdgePayload = {};
+export type ContainmentEdgeID = {
+ +type: "CONTAINMENT",
+ +childID: NodeID,
+ +parentID: NodeID,
+};
+export type ReferenceEdgePayload = {};
+export type ReferenceEdgeID = {
+type: "REFERENCE",
-|};
+ +referrer: NodeID,
+ +referent: NodeID,
+};
-export type EdgePayload = AuthorshipEdgePayload | ReferenceEdgePayload;
-export type EdgeType = $ElementType;
+export type EdgePayload =
+ | AuthorshipEdgePayload
+ | ContainmentEdgePayload
+ | ReferenceEdgePayload;
+export type EdgeID = AuthorshipEdgeID | ContainmentEdgeID | ReferenceEdgeID;
+export type EdgeType = $ElementType;
+
+export function getNodeType(n: Node): NodeType {
+ return JSON.parse(n.address.id).type;
+}
+
+export function getEdgeType(e: Edge): EdgeType {
+ return JSON.parse(e.address.id).type;
+}
+
+export class GithubParser {
+ repositoryName: string;
+ graph: Graph;
+
+ constructor(repositoryName: string) {
+ this.repositoryName = repositoryName;
+ this.graph = new Graph();
+ }
+
+ makeAddress(id: NodeID | EdgeID): Address {
+ return {
+ pluginName: GITHUB_PLUGIN_NAME,
+ repositoryName: this.repositoryName,
+ id: stringify(id),
+ };
+ }
+
+ addAuthorship(
+ authoredNodeID: IssueNodeID | PullRequestNodeID | CommentNodeID,
+ authoredNode: Node<
+ IssueNodePayload | PullRequestNodePayload | CommentNodePayload
+ >,
+ authorJson: *
+ ) {
+ let authorPayload: AuthorNodePayload = {login: authorJson.login};
+ let authorID: AuthorNodeID;
+ switch (authorJson.__typename) {
+ case "User":
+ authorID = {type: "USER", id: authorJson.id};
+ break;
+ case "Bot":
+ authorID = {type: "BOT", id: authorJson.id};
+ break;
+ case "Organization":
+ authorID = {type: "ORGANIZATION", id: authorJson.id};
+ break;
+ default:
+ throw new Error(
+ `Unexpected author type ${authorJson.__typename} on ${stringify(
+ authorJson
+ )}`
+ );
+ }
+
+ const authorNode: Node = {
+ address: this.makeAddress(authorID),
+ payload: authorPayload,
+ };
+ this.graph.addNode(authorNode);
+
+ const authorshipID = {
+ type: "AUTHORSHIP",
+ contributionID: authoredNodeID,
+ authorID,
+ };
+ const authorshipEdge: Edge = {
+ address: this.makeAddress(authorshipID),
+ payload: {},
+ src: authoredNode.address,
+ dst: authorNode.address,
+ };
+ this.graph.addEdge(authorshipEdge);
+ }
+
+ addComment(
+ parentID: IssueNodeID | PullRequestNodeID,
+ parentNode: Node,
+ commentJson: *
+ ) {
+ const commentID: CommentNodeID = {type: "COMMENT", id: commentJson.id};
+ const commentNodePayload: CommentNodePayload = {
+ body: commentJson.body,
+ url: commentJson.url,
+ };
+ const commentNode: Node = {
+ address: this.makeAddress(commentID),
+ payload: commentNodePayload,
+ };
+ this.graph.addNode(commentNode);
+
+ this.addAuthorship(commentID, commentNode, commentJson.author);
+
+ const containmentID: ContainmentEdgeID = {
+ type: "CONTAINMENT",
+ childID: commentID,
+ parentID: parentID,
+ };
+ const containmentEdge = {
+ address: this.makeAddress(containmentID),
+ payload: {},
+ src: parentNode.address,
+ dst: commentNode.address,
+ };
+ this.graph.addEdge(containmentEdge);
+ }
+
+ addIssue(issueJson: *) {
+ const issueID: IssueNodeID = {type: "ISSUE", id: issueJson.id};
+ const issuePayload: IssueNodePayload = {
+ number: issueJson.number,
+ title: issueJson.title,
+ body: issueJson.body,
+ };
+ const issueNode: Node = {
+ address: this.makeAddress(issueID),
+ payload: issuePayload,
+ };
+ this.graph.addNode(issueNode);
+
+ this.addAuthorship(issueID, issueNode, issueJson.author);
+
+ issueJson.comments.nodes.forEach((c) =>
+ this.addComment(issueID, issueNode, c)
+ );
+ }
+
+ addPullRequest(prJson: *) {
+ const pullRequestID: PullRequestNodeID = {
+ type: "PULL_REQUEST",
+ id: prJson.id,
+ };
+ const pullRequestPayload: PullRequestNodePayload = {
+ number: prJson.number,
+ title: prJson.title,
+ body: prJson.body,
+ };
+ const pullRequestNode: Node = {
+ address: this.makeAddress(pullRequestID),
+ payload: pullRequestPayload,
+ };
+ this.graph.addNode(pullRequestNode);
+
+ this.addAuthorship(pullRequestID, pullRequestNode, prJson.author);
+ prJson.comments.nodes.forEach((c) =>
+ this.addComment(pullRequestID, pullRequestNode, c)
+ );
+ }
+
+ addData(dataJson: *) {
+ dataJson.repository.issues.nodes.forEach((i) => this.addIssue(i));
+ dataJson.repository.pullRequests.nodes.forEach((pr) =>
+ this.addPullRequest(pr)
+ );
+ }
+}
diff --git a/src/backend/githubPlugin.test.js b/src/backend/githubPlugin.test.js
new file mode 100644
index 0000000..90f33f1
--- /dev/null
+++ b/src/backend/githubPlugin.test.js
@@ -0,0 +1,84 @@
+// @flow
+
+import {GithubParser, getNodeType, getEdgeType} from "./githubPlugin";
+import exampleRepoData from "./githubDemoData/example-repo.json";
+
+describe("GithubParser", () => {
+ describe("whole repo parsing", () => {
+ const parser = new GithubParser("sourcecred/example-repo");
+ parser.addData(exampleRepoData.data);
+ const graph = parser.graph;
+
+ it("parses the entire example-repo as expected", () => {
+ expect(graph).toMatchSnapshot();
+ });
+
+ it("every comment has an author and container", () => {
+ const comments = graph
+ .getAllNodes()
+ .filter((n) => getNodeType(n) === "COMMENT");
+ expect(comments).not.toHaveLength(0);
+ comments.forEach((c) => {
+ const authorEdges = graph
+ .getOutEdges(c.address)
+ .filter((e) => getEdgeType(e) === "AUTHORSHIP");
+ expect(authorEdges.length).toBe(1);
+ const containerEdges = graph
+ .getInEdges(c.address)
+ .filter((e) => getEdgeType(e) === "CONTAINMENT");
+ expect(containerEdges.length).toBe(1);
+ });
+ });
+
+ it("every pull request and issue has an author", () => {
+ const issuesAndPRs = graph
+ .getAllNodes()
+ .filter(
+ (n) => ["ISSUE", "PULL_REQUEST"].indexOf(getNodeType(n)) !== -1
+ );
+ expect(issuesAndPRs).not.toHaveLength(0);
+ issuesAndPRs.forEach((x) => {
+ const outEdges = graph.getOutEdges(x.address);
+ const authorEdges = outEdges.filter(
+ (e) => getEdgeType(e) === "AUTHORSHIP"
+ );
+ expect(authorEdges.length).toBe(1);
+ });
+ });
+ });
+
+ describe("issue parsing", () => {
+ it("parses a simple issue (https://github.com/sourcecred/example-repo/issues/1)", () => {
+ const issue1 = exampleRepoData.data.repository.issues.nodes[0];
+ expect(issue1.number).toBe(1);
+ const parser = new GithubParser("sourcecred/example-repo");
+ parser.addIssue(issue1);
+ expect(parser.graph).toMatchSnapshot();
+ });
+
+ it("parses an issue with comments (https://github.com/sourcecred/example-repo/issues/6)", () => {
+ const issue6 = exampleRepoData.data.repository.issues.nodes[3];
+ expect(issue6.number).toBe(6);
+ const parser = new GithubParser("sourcecred/example-repo");
+ parser.addIssue(issue6);
+ expect(parser.graph).toMatchSnapshot();
+ });
+ });
+
+ describe("pull request parsing", () => {
+ it("parses a simple pull request (https://github.com/sourcecred/example-repo/pull/3)", () => {
+ const pr3 = exampleRepoData.data.repository.pullRequests.nodes[0];
+ expect(pr3.number).toBe(3);
+ const parser = new GithubParser("sourcecred/example-repo");
+ parser.addPullRequest(pr3);
+ expect(parser.graph).toMatchSnapshot();
+ });
+ it("parses a pr with review comments (https://github.com/sourcecred/example-repo/pull/3)", () => {
+ const pr5 = exampleRepoData.data.repository.pullRequests.nodes[1];
+ expect(pr5.number).toBe(5);
+ const parser = new GithubParser("sourcecred/example-repo");
+ parser.addPullRequest(pr5);
+ expect(parser.graph).toMatchSnapshot();
+ });
+ });
+});