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.
This commit is contained in:
Dandelion Mané 2018-08-29 14:00:22 -07:00 committed by GitHub
parent a5c909689a
commit dda9c5feff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 117 additions and 41 deletions

View File

@ -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",
],
],

View File

@ -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",
],
}

View File

@ -183,10 +183,12 @@ Object {
"sourcecred",
"github",
"USERLIKE",
"USER",
"decentralion",
],
"structured": Object {
"login": "decentralion",
"subtype": "USER",
"type": "USERLIKE",
},
}

View File

@ -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 = {

View File

@ -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);

View File

@ -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)}`);
}

View File

@ -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"],
},
]);
});
});

View File

@ -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};