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(); + }); + }); +});