From 993de9303a03eb68210d3adb46ae11e23614b142 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Mon, 22 Oct 2018 10:01:49 -0700 Subject: [PATCH] github: translate old format to structured format (#930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This implements the translation module described in #923. See that issue for context. Test Plan: This is a mostly straightforward translation from one strongly typed data structure to another, so Flow handles most of it. As a check on the snapshot, run: ``` $ grep -e oid -e target -e mergeCommit \ > src/plugins/github/__snapshots__/translateContinuations.test.js.snap "target": Object { "oid": "6bd1b4c0b719c22c688a74863be07a699b7b9b34", "oid": "c430bd74455105f77215ece51945094ceeee6c86", "oid": "6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6", "oid": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a", "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", "oid": "ecc889dc94cf6da17ae6eab5bb7b7155f577519d", "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", "mergeCommit": Object { "oid": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a", "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", "oid": "ecc889dc94cf6da17ae6eab5bb7b7155f577519d", "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", "mergeCommit": Object { "oid": "6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6", "oid": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a", "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", "oid": "ecc889dc94cf6da17ae6eab5bb7b7155f577519d", "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", "mergeCommit": null, ``` Cross-check this against [the example-github commits][commits] thus: - Note that commit `6bd1b4c` is the head commit, and is thus the root commit of the `target` chain. - Note that commits `0a22334` and `6d5b3aa`, which were merged via pull request, appear twice each: once in the history from head, and once as the merge commit of a pull request. - Note that commit `0a22334` has two parents at each occurrence. - Note that the unmerged pull request’s merge commit is `null`. [commits]: https://github.com/sourcecred/example-github/commits/master To run this on real-world data, apply the following patch: ```diff diff --git a/src/plugins/github/fetchGithubRepo.js b/src/plugins/github/fetchGithubRepo.js index 6ac201af..b14ca760 100644 --- a/src/plugins/github/fetchGithubRepo.js +++ b/src/plugins/github/fetchGithubRepo.js @@ -11,6 +11,7 @@ import {stringify, inlineLayout, type Body} from "../../graphql/queries"; import {createQuery, createVariables, postQueryExhaustive} from "./graphql"; import type {GithubResponseJSON} from "./graphql"; import type {RepoId} from "../../core/repoId"; +import translateContinuations from "./translateContinuations"; /** * Scrape data from a GitHub repo using the GitHub API. @@ -44,6 +45,11 @@ export default function fetchGithubRepo( payload ).then((x: GithubResponseJSON) => { ensureNoMorePages(x); + console.warn("Translating continuations..."); + for (const w of translateContinuations(x).warnings) { + console.warn(w); + } + console.warn("Done."); return x; }); } ``` Then run: ``` $ yarn backend >/dev/null 2>/dev/null; echo $? 0 $ node ./bin/sourcecred.js load sourcecred/sourcecred --plugin github 2>&1 | > ts -s '%.s' 55.015740 Translating continuations... 55.037217 { type: 'UNKNOWN_PARENT_OID', 55.037273 child: '0d38dde23a6de831315f3643a7d2bc15e8df7678', 55.037290 parent: 'cb8ba0eaa1abc1f921e7165bb19e29b40723ce65' } 55.037309 { type: 'UNKNOWN_PARENT_OID', 55.037336 child: 'd152f48ce4c2ed1d046bf6ed4f139e7e393ea660', 55.037359 parent: 'de7a8723963d9cd0437ef34f5942a071b850c0e7' } 55.037383 Done. ``` Note that the two commits in question were each merged into a non-master branch, in #28 and #329 respectively. Note also that translating these continuations took just 22 milliseconds. wchargin-branch: github-translate-continuations --- .../translateContinuations.test.js.snap | 1192 +++++++++++++++++ src/plugins/github/translateContinuations.js | 331 +++++ .../github/translateContinuations.test.js | 150 +++ 3 files changed, 1673 insertions(+) create mode 100644 src/plugins/github/__snapshots__/translateContinuations.test.js.snap create mode 100644 src/plugins/github/translateContinuations.js create mode 100644 src/plugins/github/translateContinuations.test.js diff --git a/src/plugins/github/__snapshots__/translateContinuations.test.js.snap b/src/plugins/github/__snapshots__/translateContinuations.test.js.snap new file mode 100644 index 0000000..deefb1f --- /dev/null +++ b/src/plugins/github/__snapshots__/translateContinuations.test.js.snap @@ -0,0 +1,1192 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plugins/github/translateContinuations translateContinuations works on the example data 1`] = ` +Object { + "result": Object { + "__typename": "Repository", + "defaultBranchRef": Object { + "__typename": "Ref", + "id": "MDM6UmVmMTIzMjU1MDA2Om1hc3Rlcg==", + "target": Object { + "__typename": "Commit", + "author": Object { + "date": "2018-09-12T19:48:21-07:00", + "user": null, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OjZiZDFiNGMwYjcxOWMyMmM2ODhhNzQ4NjNiZTA3YTY5OWI3YjliMzQ=", + "message": "A commit from someone with no GitHub account + +Summary: +This is a commit to master by a user with email at \`example.com\`, which +should not be linked to any GitHub account. + +Generated with: + + git -c user.name='Mysterious Stranger' \\\\ + -c user.email='mysterious-stranger@example.com' \\\\ + commit -S + +Actually committed and signed by William Chargin . +Verify public key at either of: + - + - (click link to \\"My PGP key\\")", + "oid": "6bd1b4c0b719c22c688a74863be07a699b7b9b34", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-09-12T14:43:54-07:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQyODE5Mzgy", + "login": "credbot", + "url": "https://github.com/credbot", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmM0MzBiZDc0NDU1MTA1Zjc3MjE1ZWNlNTE5NDUwOTRjZWVlZTZjODY=", + "message": "Hello from credbot! + +Summary: +This is a commit to master under the name and email of credbot, who has +no other contributions to the repository. This is intended to test that +we can still pull the correct GitHub user off of the commit. + +Generated with: + + git -c user.name='credbot' \\\\ + -c user.email='42819382+credbot@users.noreply.github.com' \\\\ + commit + +Actually committed and signed by William Chargin . +Verify public key at either of: + - + - (click link to \\"My PGP key\\")", + "oid": "c430bd74455105f77215ece51945094ceeee6c86", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T20:25:54-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OjZkNWIzYWEzMWViYjY4YTA2Y2ViNDZiYmQ2Y2Y0OWI2Y2NkNmY1ZTY=", + "message": "This pull request will be more contentious. I can feel it... (#5) + +* This pull request will be more contentious. I can feel it... + +* Address wchargin's unreasonable complaints", + "oid": "6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:43:47-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OjBhMjIzMzQ2YjRlNmRlYzAxMjdiMWU2YWE4OTJjNGVlMDQyNGI2NmE=", + "message": "Merge pull request #3 from sourcecred/add-readme + +Add README, merge via PR.", + "oid": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:41:11-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjOTFhZGI3MThhNjA0NWI0OTIzMDNmMDBkOGU4YmViOTU3ZGM3ODA=", + "message": "Commit without pull request.", + "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", + "parents": Array [], + "url": "https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780", + }, + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:42:09-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjYzg4OWRjOTRjZjZkYTE3YWU2ZWFiNWJiN2I3MTU1ZjU3NzUxOWQ=", + "message": "Add README, merge via PR.", + "oid": "ecc889dc94cf6da17ae6eab5bb7b7155f577519d", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:41:11-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjOTFhZGI3MThhNjA0NWI0OTIzMDNmMDBkOGU4YmViOTU3ZGM3ODA=", + "message": "Commit without pull request.", + "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", + "parents": Array [], + "url": "https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/ecc889dc94cf6da17ae6eab5bb7b7155f577519d", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/0a223346b4e6dec0127b1e6aa892c4ee0424b66a", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/c430bd74455105f77215ece51945094ceeee6c86", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/6bd1b4c0b719c22c688a74863be07a699b7b9b34", + }, + }, + "id": "MDEwOlJlcG9zaXRvcnkxMjMyNTUwMDY=", + "issues": Array [ + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "This is just an example issue.", + "comments": Array [], + "id": "MDU6SXNzdWUzMDA5MzQ4MTg=", + "number": 1, + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "LAUGH", + "id": "MDg6UmVhY3Rpb24yOTQwMjEwNw==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HEART", + "id": "MDg6UmVhY3Rpb24yOTQwMjEwOQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "title": "An example issue.", + "url": "https://github.com/sourcecred/example-github/issues/1", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "This issue references another issue, namely #1", + "comments": Array [ + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "It should also be possible to reference by exact url: https://github.com/sourcecred/example-github/issues/6", + "id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "We might also reference individual comments directly. +https://github.com/sourcecred/example-github/issues/6#issuecomment-373768538", + "id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-373768850", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Here's a PR by direct url: https://github.com/sourcecred/example-github/pull/5", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTU3NjE4NQ==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-385576185", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "a PR review by url: https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTU3NjIyMA==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-385576220", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "a PR Review Comment by url: https://github.com/sourcecred/example-github/pull/5#discussion_r171460198", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTU3NjI0OA==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-385576248", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "a user by url: https://github.com/wchargin", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTU3NjI3Mw==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-385576273", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Here are several references: +#1 +#2 +#3 + +https://github.com/sourcecred/example-github/pull/5#discussion_r171460198 +https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899 +", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTU3NjkyMA==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-385576920", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "This comment has no references.", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTU3NjkzNg==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/2#issuecomment-385576936", + }, + ], + "id": "MDU6SXNzdWUzMDA5MzQ5ODA=", + "number": 2, + "reactions": Array [], + "title": "A referencing issue.", + "url": "https://github.com/sourcecred/example-github/issues/2", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Alas, its life as an open issue had only just begun.", + "comments": Array [], + "id": "MDU6SXNzdWUzMDA5MzYzNzQ=", + "number": 4, + "reactions": Array [], + "title": "A closed pull request", + "url": "https://github.com/sourcecred/example-github/issues/4", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "This issue shall shortly have a few comments.", + "comments": Array [ + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "A wild COMMENT appeared!", + "id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/6#issuecomment-373768442", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "And the maintainer said, \\"Let there be comments!\\"", + "id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/6#issuecomment-373768538", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "This comment references an #2, which itself references an issue. This comment is thus allows us to test that in-references are not included when requesting a Post's references.", + "id": "MDEyOklzc3VlQ29tbWVudDM4NTIyMzMxNg==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/6#issuecomment-385223316", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQyODE5Mzgy", + "login": "credbot", + "url": "https://github.com/credbot", + }, + "body": "Hi! I'm a bot! Beep boop beep!!", + "id": "MDEyOklzc3VlQ29tbWVudDQxNzEwNDA0Nw==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/6#issuecomment-417104047", + }, + ], + "id": "MDU6SXNzdWUzMDU5OTM3NzM=", + "number": 6, + "reactions": Array [], + "title": "An issue with comments", + "url": "https://github.com/sourcecred/example-github/issues/6", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Deal with this, naive string display algorithms!!!!!", + "comments": Array [], + "id": "MDU6SXNzdWUzMDY5ODM1NTI=", + "number": 7, + "reactions": Array [], + "title": "An issue with an extremely long title, which even has a VerySuperFragicalisticialiManyCharacterUberLongTriplePlusGood word in it, and should really be truncated intelligently or something", + "url": "https://github.com/sourcecred/example-github/issues/7", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️ +Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️", + "comments": Array [], + "id": "MDU6SXNzdWUzMDY5ODUzNjc=", + "number": 8, + "reactions": Array [], + "title": "Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️", + "url": "https://github.com/sourcecred/example-github/issues/8", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "This issue was paired with @wchargin + +It also attempts to reference itself: #10 + +It also references something twice: #2 #2 ", + "comments": Array [], + "id": "MDU6SXNzdWUzMzcwOTU0NzM=", + "number": 10, + "reactions": Array [], + "title": "Paired with multireference", + "url": "https://github.com/sourcecred/example-github/issues/10", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + "body": "@wchargin-ghost002, show yourself.", + "comments": Array [ + Object { + "__typename": "IssueComment", + "author": null, + "body": "Hello. :ghost: ", + "id": "MDEyOklzc3VlQ29tbWVudDQyMDgxMTg3Mg==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/11#issuecomment-420811872", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + "body": "[Here is a screenshot of the thread at this point, prior to account deletion.](https://user-images.githubusercontent.com/4317806/45455614-8c4fe600-b69c-11e8-902e-aec65d7403e6.png) + +The user has GraphQL ID \`MDQ6VXNlcjQzMjIyMTkw\` and database ID \`43222190\`. + +The previous comment has GraphQL ID \`MDEyOklzc3VlQ29tbWVudDQyMDgxMTg3Mg==\` and database \`420811872\`. +", + "id": "MDEyOklzc3VlQ29tbWVudDQyMDgxMzAxMw==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/11#issuecomment-420813013", + }, + Object { + "__typename": "IssueComment", + "author": null, + "body": "My time in this life draws to a close. :wave: ", + "id": "MDEyOklzc3VlQ29tbWVudDQyMDgxMzIwNg==", + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "THUMBS_UP", + "id": "MDg6UmVhY3Rpb24yOTQ1MDk5Nw==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "THUMBS_DOWN", + "id": "MDg6UmVhY3Rpb24yOTQ1MTAwMQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "LAUGH", + "id": "MDg6UmVhY3Rpb24yOTQ1MTAwMw==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HOORAY", + "id": "MDg6UmVhY3Rpb24yOTQ1MTAwOA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "url": "https://github.com/sourcecred/example-github/issues/11#issuecomment-420813206", + }, + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": ":skull: :ghost: +RIP", + "id": "MDEyOklzc3VlQ29tbWVudDQyMDgxMzYyMQ==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/issues/11#issuecomment-420813621", + }, + ], + "id": "MDU6SXNzdWUzNTk2Njc4Mjk=", + "number": 11, + "reactions": Array [], + "title": "An issue with a comment from a deleted user", + "url": "https://github.com/sourcecred/example-github/issues/11", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "By url: https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780 + +By hash: ecc889dc94cf6da17ae6eab5bb7b7155f577519d + +Hash from another repo: 3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f (example-git in this case)", + "comments": Array [], + "id": "MDU6SXNzdWUzNjAwOTExMDc=", + "number": 12, + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "THUMBS_UP", + "id": "MDg6UmVhY3Rpb24yOTQwMjEwMw==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "title": "An issue with commit references", + "url": "https://github.com/sourcecred/example-github/issues/12", + }, + Object { + "__typename": "Issue", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "I'm reacting to this with every emoji.", + "comments": Array [], + "id": "MDU6SXNzdWUzNjAwOTEzMTQ=", + "number": 13, + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "THUMBS_UP", + "id": "MDg6UmVhY3Rpb24yOTQwMjEzNA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "THUMBS_DOWN", + "id": "MDg6UmVhY3Rpb24yOTQwMjEzOQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "LAUGH", + "id": "MDg6UmVhY3Rpb24yOTQwMjE0MA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HOORAY", + "id": "MDg6UmVhY3Rpb24yOTQwMjE0MQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "CONFUSED", + "id": "MDg6UmVhY3Rpb24yOTQwMjE0NQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HEART", + "id": "MDg6UmVhY3Rpb24yOTQwMjE0OA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "title": "An issue with reactions", + "url": "https://github.com/sourcecred/example-github/issues/13", + }, + ], + "name": "example-github", + "owner": Object { + "__typename": "Organization", + "id": "MDEyOk9yZ2FuaXphdGlvbjM1NzExNjY3", + "login": "sourcecred", + "url": "https://github.com/sourcecred", + }, + "pullRequests": Array [ + Object { + "__typename": "PullRequest", + "additions": 1, + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Oh look, it's a pull request.", + "comments": Array [ + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "It seems apropos to reference something from a pull request comment... eg: #2 ", + "id": "MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==", + "reactions": Array [], + "url": "https://github.com/sourcecred/example-github/pull/3#issuecomment-369162222", + }, + ], + "deletions": 0, + "id": "MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx", + "mergeCommit": Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:43:47-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OjBhMjIzMzQ2YjRlNmRlYzAxMjdiMWU2YWE4OTJjNGVlMDQyNGI2NmE=", + "message": "Merge pull request #3 from sourcecred/add-readme + +Add README, merge via PR.", + "oid": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:41:11-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjOTFhZGI3MThhNjA0NWI0OTIzMDNmMDBkOGU4YmViOTU3ZGM3ODA=", + "message": "Commit without pull request.", + "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", + "parents": Array [], + "url": "https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780", + }, + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:42:09-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjYzg4OWRjOTRjZjZkYTE3YWU2ZWFiNWJiN2I3MTU1ZjU3NzUxOWQ=", + "message": "Add README, merge via PR.", + "oid": "ecc889dc94cf6da17ae6eab5bb7b7155f577519d", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:41:11-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjOTFhZGI3MThhNjA0NWI0OTIzMDNmMDBkOGU4YmViOTU3ZGM3ODA=", + "message": "Commit without pull request.", + "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", + "parents": Array [], + "url": "https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/ecc889dc94cf6da17ae6eab5bb7b7155f577519d", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/0a223346b4e6dec0127b1e6aa892c4ee0424b66a", + }, + "number": 3, + "reactions": Array [], + "reviews": Array [], + "title": "Add README, merge via PR.", + "url": "https://github.com/sourcecred/example-github/pull/3", + }, + Object { + "__typename": "PullRequest", + "additions": 1, + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "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", + "comments": Array [ + Object { + "__typename": "IssueComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + "body": "In retrospect, this was an excellent PR.", + "id": "MDEyOklzc3VlQ29tbWVudDM5NjQzMDQ2NA==", + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "HEART", + "id": "MDg6UmVhY3Rpb24yOTQ1MTA1Mw==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "CONFUSED", + "id": "MDg6UmVhY3Rpb24yOTQ1MTA1NQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "url": "https://github.com/sourcecred/example-github/pull/5#issuecomment-396430464", + }, + ], + "deletions": 0, + "id": "MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy", + "mergeCommit": Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T20:25:54-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OjZkNWIzYWEzMWViYjY4YTA2Y2ViNDZiYmQ2Y2Y0OWI2Y2NkNmY1ZTY=", + "message": "This pull request will be more contentious. I can feel it... (#5) + +* This pull request will be more contentious. I can feel it... + +* Address wchargin's unreasonable complaints", + "oid": "6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:43:47-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OjBhMjIzMzQ2YjRlNmRlYzAxMjdiMWU2YWE4OTJjNGVlMDQyNGI2NmE=", + "message": "Merge pull request #3 from sourcecred/add-readme + +Add README, merge via PR.", + "oid": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:41:11-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjOTFhZGI3MThhNjA0NWI0OTIzMDNmMDBkOGU4YmViOTU3ZGM3ODA=", + "message": "Commit without pull request.", + "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", + "parents": Array [], + "url": "https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780", + }, + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:42:09-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjYzg4OWRjOTRjZjZkYTE3YWU2ZWFiNWJiN2I3MTU1ZjU3NzUxOWQ=", + "message": "Add README, merge via PR.", + "oid": "ecc889dc94cf6da17ae6eab5bb7b7155f577519d", + "parents": Array [ + Object { + "__typename": "Commit", + "author": Object { + "date": "2018-02-28T00:41:11-08:00", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + "id": "MDY6Q29tbWl0MTIzMjU1MDA2OmVjOTFhZGI3MThhNjA0NWI0OTIzMDNmMDBkOGU4YmViOTU3ZGM3ODA=", + "message": "Commit without pull request.", + "oid": "ec91adb718a6045b492303f00d8e8beb957dc780", + "parents": Array [], + "url": "https://github.com/sourcecred/example-github/commit/ec91adb718a6045b492303f00d8e8beb957dc780", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/ecc889dc94cf6da17ae6eab5bb7b7155f577519d", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/0a223346b4e6dec0127b1e6aa892c4ee0424b66a", + }, + ], + "url": "https://github.com/sourcecred/example-github/commit/6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6", + }, + "number": 5, + "reactions": Array [], + "reviews": Array [ + Object { + "__typename": "PullRequestReview", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + "body": "hmmm.jpg", + "comments": Array [ + Object { + "__typename": "PullRequestReviewComment", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + "body": "seems a bit capricious", + "id": "MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE3MTQ2MDE5OA==", + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "THUMBS_UP", + "id": "MDg6UmVhY3Rpb24yOTU5NTI1MQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + }, + Object { + "__typename": "Reaction", + "content": "THUMBS_DOWN", + "id": "MDg6UmVhY3Rpb24yOTU5NTI1NA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + }, + Object { + "__typename": "Reaction", + "content": "THUMBS_UP", + "id": "MDg6UmVhY3Rpb24yOTU5NTI1NQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "LAUGH", + "id": "MDg6UmVhY3Rpb24yOTU5NTI1Nw==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + }, + Object { + "__typename": "Reaction", + "content": "THUMBS_DOWN", + "id": "MDg6UmVhY3Rpb24yOTU5NTI1OA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HOORAY", + "id": "MDg6UmVhY3Rpb24yOTU5NTI1OQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + }, + Object { + "__typename": "Reaction", + "content": "LAUGH", + "id": "MDg6UmVhY3Rpb24yOTU5NTI2MQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "CONFUSED", + "id": "MDg6UmVhY3Rpb24yOTU5NTI2NQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + }, + Object { + "__typename": "Reaction", + "content": "HOORAY", + "id": "MDg6UmVhY3Rpb24yOTU5NTI2Ng==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HEART", + "id": "MDg6UmVhY3Rpb24yOTU5NTI2OA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + }, + Object { + "__typename": "Reaction", + "content": "CONFUSED", + "id": "MDg6UmVhY3Rpb24yOTU5NTI2OQ==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "HEART", + "id": "MDg6UmVhY3Rpb24yOTU5NTI3NA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "url": "https://github.com/sourcecred/example-github/pull/5#discussion_r171460198", + }, + ], + "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MTAwMzEzODk5", + "state": "CHANGES_REQUESTED", + "url": "https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899", + }, + Object { + "__typename": "PullRequestReview", + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjQzMTc4MDY=", + "login": "wchargin", + "url": "https://github.com/wchargin", + }, + "body": "I'm sold", + "comments": Array [], + "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MTAwMzE0MDM4", + "state": "APPROVED", + "url": "https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100314038", + }, + ], + "title": "This pull request will be more contentious. I can feel it...", + "url": "https://github.com/sourcecred/example-github/pull/5", + }, + Object { + "__typename": "PullRequest", + "additions": 3, + "author": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + "body": "Nominally paired with @wchargin", + "comments": Array [], + "deletions": 0, + "id": "MDExOlB1bGxSZXF1ZXN0MTg1ODA2MTU3", + "mergeCommit": null, + "number": 9, + "reactions": Array [ + Object { + "__typename": "Reaction", + "content": "HEART", + "id": "MDg6UmVhY3Rpb24yOTQ1MTAxMg==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + Object { + "__typename": "Reaction", + "content": "THUMBS_UP", + "id": "MDg6UmVhY3Rpb24yOTQ1MTAxNA==", + "user": Object { + "__typename": "User", + "id": "MDQ6VXNlcjE0MDAwMjM=", + "login": "decentralion", + "url": "https://github.com/decentralion", + }, + }, + ], + "reviews": Array [], + "title": "An unmerged pull request", + "url": "https://github.com/sourcecred/example-github/pull/9", + }, + ], + "url": "https://github.com/sourcecred/example-github", + }, + "warnings": Array [], +} +`; diff --git a/src/plugins/github/translateContinuations.js b/src/plugins/github/translateContinuations.js new file mode 100644 index 0000000..75745f1 --- /dev/null +++ b/src/plugins/github/translateContinuations.js @@ -0,0 +1,331 @@ +// @flow +// Temporary module to translate GraphQL results from the old format +// with manually resolved continuations to the format emitted by the +// Mirror module. See issue #923 for context. + +import type { + AuthorJSON, + BotJSON, + CommentJSON, + CommitJSON, + GitObjectJSON, + GithubResponseJSON, + IssueJSON, + OrganizationJSON, + PullJSON, + ReactionJSON, + RefJSON, + RepositoryJSON, + ReviewCommentJSON, + ReviewJSON, + UserJSON, +} from "./graphql"; +import type { + Actor, + Blob, + Bot, + Commit, + GitObject, + GitObjectID, + Issue, + IssueComment, + Organization, + PullRequest, + PullRequestReview, + PullRequestReviewComment, + Reaction, + Ref, + Repository, + RepositoryOwner, + Tag, + Tree, + User, +} from "./graphqlTypes"; + +export type Warning = + // We've never seen it happen, and don't know how it could. But the + // GitHub schema says that it can. This warning is more of a + // diagnostic to the SourceCred maintainers (if it comes up on a real + // repository, we can learn something!) than an indication that + // something has gone wrong. + | {|+type: "NON_COMMIT_REF_TARGET", +target: GitObjectJSON|} + // This can happen if a commit has a parent that we did not fetch. We + // only fetch commits that are Git-reachable from HEAD or are the direct + // merge commit of a pull request. We may therefore omit commits that + // disappeared from master after a force-push, or were an ancestor of a + // pull request that was merged into a branch other than master. See + // issue #923 for more context. If this is omitted, we will simply + // omit the offending parent commit. + | {|+type: "UNKNOWN_PARENT_OID", +child: GitObjectID, +parent: GitObjectID|}; + +export default function translate( + json: GithubResponseJSON +): {| + +result: Repository, + +warnings: $ReadOnlyArray, +|} { + const repositoryJson = json.repository; + const warnings: Array = []; + + // Most of the work that this function does is exploding connections + // into lists of nodes. But commits require some special attention, + // because we have to resolve parent OIDs to actual parent commits. + // This means that it is most convenient to start by discovering all + // commits in the data. + const commits: Map< + GitObjectID, + {| + ...Commit, + parents: Array, // mutable: we build this incrementally + |} + > = new Map(); + + // First, create all the commit objects, initializing them with empty + // parent arrays. We put these temporarily into a map keyed by OID for + // deduplication: a commit may appear both in the linearized history + // from HEAD and also as the merge commit of a pull request, and we + // want to process it just once. + const commitJsons: $ReadOnlyArray = Array.from( + new Map( + Array.from( + (function*() { + if (repositoryJson.defaultBranchRef) { + const target = repositoryJson.defaultBranchRef.target; + switch (target.__typename) { + case "Commit": + yield* target.history.nodes; + break; + case "Tree": + case "Blob": + case "Tag": + warnings.push({type: "NON_COMMIT_REF_TARGET", target}); + break; + // istanbul ignore next: unreachable per Flow + default: + throw new Error((target.type: empty)); + } + } + for (const pull of repositoryJson.pulls.nodes) { + if (pull.mergeCommit) { + yield pull.mergeCommit; + } + } + })() + ).map((json) => [json.oid, json]) + ).values() + ); + for (const commitJson of commitJsons) { + const commit = { + __typename: "Commit", + author: {...commitJson.author}, + id: commitJson.id, + message: commitJson.message, + oid: commitJson.oid, + parents: [], + url: commitJson.url, + }; + commits.set(commit.oid, commit); + } + + // Then, once all the objects have been created, we can set up the + // parents. + for (const commitJson of commitJsons) { + const commit = commits.get(commitJson.oid); + // istanbul ignore next: should not be possible + if (commit == null) { + throw new Error( + "invariant violation: commit came out of nowhere: " + commitJson.oid + ); + } + for (const {oid: parentOid} of commitJson.parents.nodes) { + const parentCommit = commits.get(parentOid); + if (parentCommit == null) { + warnings.push({ + type: "UNKNOWN_PARENT_OID", + child: commitJson.oid, + parent: parentOid, + }); + } else { + commit.parents.push(parentCommit); + } + } + } + + // The rest is mostly mechanical. The pattern is: we pull off and + // recursively translate the non-primitive fields of each object, and + // then add a typename and put back the primitives. For union types, + // we switch on the __typename and dispatch to the appropriate object + // translators. + + function translateRepository(json: RepositoryJSON): Repository { + const {defaultBranchRef, issues, owner, pulls, ...rest} = json; + return { + __typename: "Repository", + defaultBranchRef: + defaultBranchRef == null + ? null + : translateDefaultBranchRef(defaultBranchRef), + issues: issues.nodes.map(translateIssue), + owner: translateRepositoryOwner(owner), + pullRequests: pulls.nodes.map(translatePullRequest), + ...rest, + }; + } + + function translateDefaultBranchRef(json: RefJSON): Ref { + const {target, ...rest} = json; + return { + __typename: "Ref", + target: translateDefaultBranchRefTarget(target), + ...rest, + }; + } + + // This one is a bit wonky, because our `GitObjectJSON` type is not a + // good representation of the GitHub schema. In particular, a + // `GitObjectJSON` can represent a commit, but in a different form + // than our `CommitJSON`! This function _only_ applies to + // `GitObjectJSON`s that we fetched as the `target` of the + // `defaultBranchRef` of a repository. But these are the only + // `GitObjectJSON`s that we fetch, so it's okay. + function translateDefaultBranchRefTarget(json: GitObjectJSON): GitObject { + switch (json.__typename) { + case "Commit": + // The default branch ref is `null` if there are no commits, so + // the history must include at least one commit (the HEAD + // commit). + return lookUpCommit(json.history.nodes[0].oid); + case "Blob": + return ({...json}: Blob); + case "Tag": + return ({...json}: Tag); + case "Tree": + return ({...json}: Tree); + // istanbul ignore next: unreachable per Flow + default: + throw new Error((json.__typename: empty)); + } + } + + function lookUpCommit(oid: GitObjectID): Commit { + const commit = commits.get(oid); + // istanbul ignore if: unreachable: we explored all commits in + // the response, including this one. + if (commit == null) { + throw new Error("invariant violation: unknown commit: " + oid); + } + return commit; + } + + function translateCommit(json: CommitJSON): Commit { + return lookUpCommit(json.oid); + } + + function translateIssue(json: IssueJSON): Issue { + const {author, comments, reactions, ...rest} = json; + return { + __typename: "Issue", + author: author == null ? null : translateActor(author), + comments: comments.nodes.map(translateIssueComment), + reactions: reactions.nodes.map(translateReaction), + ...rest, + }; + } + + function translateIssueComment(json: CommentJSON): IssueComment { + const {author, reactions, ...rest} = json; + return { + __typename: "IssueComment", + author: author == null ? null : translateActor(author), + reactions: reactions.nodes.map(translateReaction), + ...rest, + }; + } + + function translateReaction(json: ReactionJSON): Reaction { + const {user, ...rest} = json; + return { + __typename: "Reaction", + user: user == null ? null : translateUser(user), + ...rest, + }; + } + + function translateRepositoryOwner( + json: UserJSON | OrganizationJSON + ): RepositoryOwner { + switch (json.__typename) { + case "User": + return translateUser(json); + case "Organization": + return translateOrganization(json); + // istanbul ignore next: unreachable per Flow + default: + throw new Error((json.__typename: empty)); + } + } + + function translateActor(json: AuthorJSON): Actor { + switch (json.__typename) { + case "User": + return translateUser(json); + case "Organization": + return translateOrganization(json); + case "Bot": + return translateBot(json); + // istanbul ignore next: unreachable per Flow + default: + throw new Error((json.__typename: empty)); + } + } + + function translateUser(json: UserJSON): User { + return {...json}; + } + + function translateOrganization(json: OrganizationJSON): Organization { + return {...json}; + } + + function translateBot(json: BotJSON): Bot { + return {...json}; + } + + function translatePullRequest(json: PullJSON): PullRequest { + const {author, comments, mergeCommit, reactions, reviews, ...rest} = json; + return { + __typename: "PullRequest", + author: author == null ? null : translateActor(author), + comments: comments.nodes.map(translateIssueComment), + mergeCommit: mergeCommit == null ? null : translateCommit(mergeCommit), + reactions: reactions.nodes.map(translateReaction), + reviews: reviews.nodes.map(translatePullRequestReview), + ...rest, + }; + } + + function translatePullRequestReview(json: ReviewJSON): PullRequestReview { + const {author, comments, ...rest} = json; + return { + __typename: "PullRequestReview", + author: author == null ? null : translateActor(author), + comments: comments.nodes.map(translatePullRequestReviewComment), + ...rest, + }; + } + + function translatePullRequestReviewComment( + json: ReviewCommentJSON + ): PullRequestReviewComment { + const {author, reactions, ...rest} = json; + return { + __typename: "PullRequestReviewComment", + author: author == null ? null : translateActor(author), + reactions: reactions.nodes.map(translateReaction), + ...rest, + }; + } + + const result = translateRepository(repositoryJson); + return {result, warnings}; +} diff --git a/src/plugins/github/translateContinuations.test.js b/src/plugins/github/translateContinuations.test.js new file mode 100644 index 0000000..f338f2f --- /dev/null +++ b/src/plugins/github/translateContinuations.test.js @@ -0,0 +1,150 @@ +// @flow + +import {exampleData} from "./example/example"; + +import translateContinuations from "./translateContinuations"; + +describe("plugins/github/translateContinuations", () => { + describe("translateContinuations", () => { + it("works on the example data", () => { + expect(translateContinuations(exampleData())).toMatchSnapshot(); + }); + + it("raises a warning if the defaultBranchRef is not a commit", () => { + const exampleData = { + repository: { + defaultBranchRef: { + id: "ref-id", + target: { + __typename: "Tree", + id: "tree-id", + oid: "123", + }, + }, + id: "repo-id", + issues: { + nodes: [], + pageInfo: {hasNextPage: false, endCursor: null}, + }, + name: "bar", + owner: { + __typename: "User", + id: "user-id", + login: "foo", + url: "https://github.com/foo", + }, + pulls: { + nodes: [], + pageInfo: {hasNextPage: false, endCursor: null}, + }, + url: "https://github.com/foo/bar", + }, + }; + const {result, warnings} = translateContinuations(exampleData); + expect(result.defaultBranchRef).toEqual({ + __typename: "Ref", + id: "ref-id", + target: {__typename: "Tree", id: "tree-id", oid: "123"}, + }); + expect(warnings).toEqual([ + { + type: "NON_COMMIT_REF_TARGET", + target: {__typename: "Tree", id: "tree-id", oid: "123"}, + }, + ]); + }); + + it("raises a warning if there is an unknown commit", () => { + const exampleData = { + repository: { + defaultBranchRef: null, + id: "repo-id", + issues: { + nodes: [], + pageInfo: {hasNextPage: false, endCursor: null}, + }, + name: "bar", + owner: { + __typename: "User", + id: "user-id", + login: "foo", + url: "https://github.com/foo", + }, + pulls: { + nodes: [ + { + id: "pr-id", + number: 1, + author: { + __typename: "Bot", + id: "bot-id", + login: "baz", + url: "https://github.com/baz", + }, + additions: 7, + deletions: 9, + comments: { + nodes: [], + pageInfo: {hasNextPage: false, endCursor: null}, + }, + reviews: { + nodes: [], + pageInfo: {hasNextPage: false, endCursor: null}, + }, + reactions: { + nodes: [], + pageInfo: {hasNextPage: false, endCursor: null}, + }, + mergeCommit: { + id: "commit-id", + author: { + date: "2001-02-03T04:05:06", + user: null, + }, + message: "where are my parents?", + oid: "456", + parents: { + nodes: [{oid: "789"}], + pageInfo: {hasNextPage: false, endCursor: "cursor-parents"}, + }, + url: "https://github.com/foo/bar/commit/456", + }, + title: "something", + body: "whatever", + url: "https://github.com/foo/bar/pull/1", + }, + ], + pageInfo: {hasNextPage: false, endCursor: "cursor-pulls"}, + }, + url: "https://github.com/foo/bar", + }, + }; + const {result, warnings} = translateContinuations(exampleData); + const pr = result.pullRequests[0]; + if (pr == null) { + throw new Error(String(pr)); + } + expect(pr.mergeCommit).toEqual({ + __typename: "Commit", + id: "commit-id", + author: { + date: "2001-02-03T04:05:06", + user: null, + }, + message: "where are my parents?", + oid: "456", + parents: [ + /* empty! */ + ], + url: "https://github.com/foo/bar/commit/456", + }); + expect(warnings).toEqual([ + { + type: "UNKNOWN_PARENT_OID", + child: "456", + parent: "789", + }, + ]); + }); + }); +});