From dda9c5feffe460bba5323c83c409eba6dcb2ecaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Wed, 29 Aug 2018 14:00:22 -0700 Subject: [PATCH] Subtype GitHub userlikes for Users and Bots (#713) Userlikes now have an additional piece of data encoded in their address: whether they are a USER or a BOT. Userlikes are still handled identically by the RelationalView, which cuts down on code duplication. I haven't added ORGANIZATIONs but it will be trivial to do once we're interested in tracking them. Note that this is basically the same as how we treat comments: comments are subtyped to review comments, issue comments, and pull comments. This is the initial step towards solving #696. Test plan: Existing unit tests pass (and caught a few bugs during development!). New test cases were added to the parser. Observe that all the snapshot changes make sense. Note: As of this commit, every GitHub userlike is classified as a user, and the subtypes are not used in the application, so this commit causes no change in observable behavior. --- .../__snapshots__/createGraph.test.js.snap | 92 +++++++++++++------ .../github/__snapshots__/edges.test.js.snap | 4 +- .../github/__snapshots__/nodes.test.js.snap | 2 + src/plugins/github/edges.test.js | 6 +- src/plugins/github/graphView.test.js | 19 +++- src/plugins/github/nodes.js | 23 ++++- src/plugins/github/nodes.test.js | 10 +- src/plugins/github/relationalView.js | 2 + 8 files changed, 117 insertions(+), 41 deletions(-) diff --git a/src/plugins/github/__snapshots__/createGraph.test.js.snap b/src/plugins/github/__snapshots__/createGraph.test.js.snap index baa66fb..9bb9107 100644 --- a/src/plugins/github/__snapshots__/createGraph.test.js.snap +++ b/src/plugins/github/__snapshots__/createGraph.test.js.snap @@ -13,8 +13,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -30,8 +31,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -47,8 +49,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -64,8 +67,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -81,8 +85,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -98,8 +103,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -115,8 +121,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -132,8 +139,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "PULL", @@ -149,8 +157,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "PULL", @@ -166,8 +175,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "PULL", @@ -183,8 +193,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -202,8 +213,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -221,8 +233,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -240,8 +253,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -259,8 +273,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -278,8 +293,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -297,8 +313,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -316,8 +333,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -335,8 +353,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -354,8 +373,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -373,8 +393,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -392,8 +413,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "6", "COMMENT", @@ -411,8 +433,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "wchargin", "4", "ISSUE", @@ -428,8 +451,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "wchargin", "4", "PULL", @@ -445,8 +469,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "wchargin", "5", "REVIEW", @@ -463,8 +488,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "wchargin", "5", "REVIEW", @@ -481,8 +507,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "wchargin", "6", "COMMENT", @@ -500,8 +527,9 @@ Array [ "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "wchargin", "7", "COMMENT", @@ -1005,8 +1033,9 @@ Array [ "sourcecred", "example-github", "5", - "2", + "3", "USERLIKE", + "USER", "wchargin", ], "dstIndex": 30, @@ -1135,8 +1164,9 @@ Array [ "example-github", "2", "385576273", - "2", + "3", "USERLIKE", + "USER", "wchargin", ], "dstIndex": 30, @@ -1557,12 +1587,14 @@ Array [ "sourcecred", "github", "USERLIKE", + "USER", "decentralion", ], Array [ "sourcecred", "github", "USERLIKE", + "USER", "wchargin", ], ], diff --git a/src/plugins/github/__snapshots__/edges.test.js.snap b/src/plugins/github/__snapshots__/edges.test.js.snap index d6dd066..f50e89f 100644 --- a/src/plugins/github/__snapshots__/edges.test.js.snap +++ b/src/plugins/github/__snapshots__/edges.test.js.snap @@ -6,8 +6,9 @@ Object { "sourcecred", "github", "AUTHORS", - "2", + "3", "USERLIKE", + "USER", "decentralion", "4", "ISSUE", @@ -27,6 +28,7 @@ Object { "sourcecred", "github", "USERLIKE", + "USER", "decentralion", ], } diff --git a/src/plugins/github/__snapshots__/nodes.test.js.snap b/src/plugins/github/__snapshots__/nodes.test.js.snap index 740ce29..ed31102 100644 --- a/src/plugins/github/__snapshots__/nodes.test.js.snap +++ b/src/plugins/github/__snapshots__/nodes.test.js.snap @@ -183,10 +183,12 @@ Object { "sourcecred", "github", "USERLIKE", + "USER", "decentralion", ], "structured": Object { "login": "decentralion", + "subtype": "USER", "type": "USERLIKE", }, } diff --git a/src/plugins/github/edges.test.js b/src/plugins/github/edges.test.js index 069f922..9b8cb96 100644 --- a/src/plugins/github/edges.test.js +++ b/src/plugins/github/edges.test.js @@ -39,7 +39,11 @@ describe("plugins/github/edges", () => { parent: nodeExamples.review(), id: "171460198", }), - user: () => ({type: GN.USERLIKE_TYPE, login: "decentralion"}), + user: () => ({ + type: GN.USERLIKE_TYPE, + subtype: "USER", + login: "decentralion", + }), }; const edgeExamples = { diff --git a/src/plugins/github/graphView.test.js b/src/plugins/github/graphView.test.js index b04785c..4814eb3 100644 --- a/src/plugins/github/graphView.test.js +++ b/src/plugins/github/graphView.test.js @@ -11,8 +11,16 @@ function exampleView() { return new GraphView(exampleGraph()); } -const decentralion = {type: "USERLIKE", login: "decentralion"}; -const wchargin = {type: "USERLIKE", login: "wchargin"}; +const decentralion: GN.UserlikeAddress = { + type: "USERLIKE", + subtype: "USER", + login: "decentralion", +}; +const wchargin: GN.UserlikeAddress = { + type: "USERLIKE", + subtype: "USER", + login: "wchargin", +}; describe("plugins/github/graphView", () => { const view = exampleView(); @@ -141,6 +149,7 @@ describe("plugins/github/graphView", () => { describe("invariants", () => { const userlike: GN.UserlikeAddress = { type: "USERLIKE", + subtype: "USER", login: "decentralion", }; const repo: GN.RepoAddress = { @@ -232,7 +241,11 @@ describe("plugins/github/graphView", () => { failsForEdge(badEdge); }); it("src must be author in edge address", () => { - const otherAuthor = {type: "USERLIKE", login: "wchargin"}; + const otherAuthor = { + type: "USERLIKE", + subtype: "USER", + login: "wchargin", + }; const authorsEdge = GE.createEdge.authors(otherAuthor, issue); (authorsEdge: any).src = GN.toRaw(userlike); const g = exampleWithParents().addEdge(authorsEdge); diff --git a/src/plugins/github/nodes.js b/src/plugins/github/nodes.js index 5e60818..b176a3b 100644 --- a/src/plugins/github/nodes.js +++ b/src/plugins/github/nodes.js @@ -15,6 +15,8 @@ export const PULL_TYPE: "PULL" = "PULL"; export const REVIEW_TYPE: "REVIEW" = "REVIEW"; export const COMMENT_TYPE: "COMMENT" = "COMMENT"; export const USERLIKE_TYPE: "USERLIKE" = "USERLIKE"; +export const USER_SUBTYPE: "USER" = "USER"; +export const BOT_SUBTYPE: "BOT" = "BOT"; export const _Prefix = Object.freeze({ base: GITHUB_PREFIX, @@ -24,6 +26,8 @@ export const _Prefix = Object.freeze({ review: _githubAddress(REVIEW_TYPE), comment: _githubAddress(COMMENT_TYPE), userlike: _githubAddress(USERLIKE_TYPE), + user: _githubAddress(USERLIKE_TYPE, USER_SUBTYPE), + bot: _githubAddress(USERLIKE_TYPE, BOT_SUBTYPE), reviewComment: _githubAddress(COMMENT_TYPE, REVIEW_TYPE), issueComment: _githubAddress(COMMENT_TYPE, ISSUE_TYPE), pullComment: _githubAddress(COMMENT_TYPE, PULL_TYPE), @@ -56,6 +60,7 @@ export type CommentAddress = {| |}; export type UserlikeAddress = {| +type: typeof USERLIKE_TYPE, + +subtype: typeof USER_SUBTYPE | typeof BOT_SUBTYPE, +login: string, |}; @@ -196,11 +201,14 @@ export function fromRaw(x: RawAddress): StructuredAddress { } } case USERLIKE_TYPE: { - if (rest.length !== 1) { + if (rest.length !== 2) { throw fail(); } - const [login] = rest; - return {type: USERLIKE_TYPE, login}; + const [subtype, login] = rest; + if (subtype !== "USER" && subtype !== "BOT") { + throw fail(); + } + return {type: USERLIKE_TYPE, subtype, login}; } default: throw fail(); @@ -264,7 +272,14 @@ export function toRaw(x: StructuredAddress): RawAddress { throw new Error(`Bad comment parent type: ${(x.parent.type: empty)}`); } case USERLIKE_TYPE: - return NodeAddress.append(_Prefix.userlike, x.login); + switch (x.subtype) { + case "BOT": + return NodeAddress.append(_Prefix.bot, x.login); + case "USER": + return NodeAddress.append(_Prefix.user, x.login); + default: + throw new Error((x.subtype: empty)); + } default: throw new Error(`Unexpected type ${(x.type: empty)}`); } diff --git a/src/plugins/github/nodes.test.js b/src/plugins/github/nodes.test.js index 5e824d5..f52c004 100644 --- a/src/plugins/github/nodes.test.js +++ b/src/plugins/github/nodes.test.js @@ -42,6 +42,7 @@ describe("plugins/github/nodes", () => { }); const user = (): GN.UserlikeAddress => ({ type: GN.USERLIKE_TYPE, + subtype: "USER", login: "decentralion", }); @@ -205,8 +206,13 @@ describe("plugins/github/nodes", () => { }); describe("userlike", () => { checkBadCases([ - {name: "no login", parts: [GN.USERLIKE_TYPE]}, - {name: "extra parts", parts: ["decentra", "lion"]}, + {name: "no subtype", parts: [GN.USERLIKE_TYPE]}, + {name: "bad subtype", parts: [GN.USERLIKE_TYPE, "FOO"]}, + {name: "no login", parts: [GN.USERLIKE_TYPE, GN.USER_SUBTYPE]}, + { + name: "extra parts", + parts: [GN.USERLIKE_TYPE, GN.USER_SUBTYPE, "decentra", "lion"], + }, ]); }); }); diff --git a/src/plugins/github/relationalView.js b/src/plugins/github/relationalView.js index 54bbe3a..c6cd823 100644 --- a/src/plugins/github/relationalView.js +++ b/src/plugins/github/relationalView.js @@ -344,6 +344,8 @@ export class RelationalView { } else { const address: UserlikeAddress = { type: N.USERLIKE_TYPE, + // TODO: Detect bots and give them a different subtype (#696) + subtype: N.USER_SUBTYPE, login: json.login, }; const entry: UserlikeEntry = {address, url: json.url};