diff --git a/src/plugins/discourse/createGraph.js b/src/plugins/discourse/createGraph.js index 310395a..44908ec 100644 --- a/src/plugins/discourse/createGraph.js +++ b/src/plugins/discourse/createGraph.js @@ -8,7 +8,13 @@ import { type Edge, type NodeAddressT, } from "../../core/graph"; -import {type PostId, type TopicId, type Post, type Topic} from "./fetch"; +import { + type PostId, + type TopicId, + type Post, + type Topic, + type LikeAction, +} from "./fetch"; import {type DiscourseData} from "./mirror"; import { topicNodeType, @@ -18,6 +24,7 @@ import { authorsTopicEdgeType, postRepliesEdgeType, topicContainsPostEdgeType, + likesEdgeType, } from "./declaration"; export function topicAddress(serverUrl: string, id: TopicId): NodeAddressT { @@ -114,7 +121,6 @@ export function postRepliesEdge( String(post.id), String(basePostId) ); - return { address, timestampMs: post.timestampMs, @@ -123,6 +129,21 @@ export function postRepliesEdge( }; } +export function likesEdge(serverUrl: string, like: LikeAction): Edge { + const address = EdgeAddress.append( + likesEdgeType.prefix, + serverUrl, + like.username, + String(like.postId) + ); + return { + address, + timestampMs: like.timestampMs, + src: userAddress(serverUrl, like.username), + dst: postAddress(serverUrl, like.postId), + }; +} + export function createGraph(serverUrl: string, data: DiscourseData): Graph { if (serverUrl.endsWith("/")) { throw new Error(`by convention, serverUrl should not end with /`); @@ -161,5 +182,9 @@ export function createGraph(serverUrl: string, data: DiscourseData): Graph { } } + for (const like of data.likes()) { + g.addEdge(likesEdge(serverUrl, like)); + } + return g; } diff --git a/src/plugins/discourse/createGraph.test.js b/src/plugins/discourse/createGraph.test.js index a37e715..d35e259 100644 --- a/src/plugins/discourse/createGraph.test.js +++ b/src/plugins/discourse/createGraph.test.js @@ -13,6 +13,9 @@ import { authorsPostEdge, topicContainsPostEdge, postRepliesEdge, + likesEdge, + userAddress, + postAddress, } from "./createGraph"; import { userNodeType, @@ -22,6 +25,7 @@ import { authorsPostEdgeType, topicContainsPostEdgeType, postRepliesEdgeType, + likesEdgeType, } from "./declaration"; import type {EdgeType, NodeType} from "../../analysis/types"; @@ -98,7 +102,7 @@ describe("plugins/discourse/createGraph", () => { authorUsername: "mzargham", }; const likes: $ReadOnlyArray = [ - {timestampMs: 3, username: "mzargam", postId: 2}, + {timestampMs: 3, username: "mzargham", postId: 2}, {timestampMs: 4, username: "decentralion", postId: 3}, ]; const posts = [post1, post2, post3]; @@ -116,14 +120,14 @@ describe("plugins/discourse/createGraph", () => { ); expect(node.timestampMs).toEqual(null); expect(NodeAddress.toParts(node.address)).toMatchInlineSnapshot(` - Array [ - "sourcecred", - "discourse", - "user", - "https://url.com", - "decentralion", - ] - `); + Array [ + "sourcecred", + "discourse", + "user", + "https://url.com", + "decentralion", + ] + `); }); it("for topics", () => { const {url, topic} = example(); @@ -133,14 +137,14 @@ describe("plugins/discourse/createGraph", () => { ); expect(node.timestampMs).toEqual(topic.timestampMs); expect(NodeAddress.toParts(node.address)).toMatchInlineSnapshot(` - Array [ - "sourcecred", - "discourse", - "topic", - "https://url.com", - "1", - ] - `); + Array [ + "sourcecred", + "discourse", + "topic", + "https://url.com", + "1", + ] + `); }); it("for posts", () => { const {url, topic, posts} = example(); @@ -150,14 +154,14 @@ describe("plugins/discourse/createGraph", () => { ); expect(node.timestampMs).toEqual(posts[1].timestampMs); expect(NodeAddress.toParts(node.address)).toMatchInlineSnapshot(` - Array [ - "sourcecred", - "discourse", - "post", - "https://url.com", - "2", - ] - `); + Array [ + "sourcecred", + "discourse", + "post", + "https://url.com", + "2", + ] + `); }); it("gives an [unknown topic] description for posts without a matching topic", () => { const post = { @@ -187,16 +191,16 @@ describe("plugins/discourse/createGraph", () => { expect(edge.dst).toEqual(expectedDst); expect(edge.timestampMs).toEqual(topic.timestampMs); expect(EdgeAddress.toParts(edge.address)).toMatchInlineSnapshot(` - Array [ - "sourcecred", - "discourse", - "authors", - "topic", - "https://url.com", - "decentralion", - "1", - ] - `); + Array [ + "sourcecred", + "discourse", + "authors", + "topic", + "https://url.com", + "decentralion", + "1", + ] + `); }); it("for authorsPost", () => { const {url, posts, topic} = example(); @@ -208,16 +212,16 @@ describe("plugins/discourse/createGraph", () => { expect(edge.dst).toEqual(expectedDst); expect(edge.timestampMs).toEqual(post.timestampMs); expect(EdgeAddress.toParts(edge.address)).toMatchInlineSnapshot(` - Array [ - "sourcecred", - "discourse", - "authors", - "post", - "https://url.com", - "wchargin", - "2", - ] - `); + Array [ + "sourcecred", + "discourse", + "authors", + "post", + "https://url.com", + "wchargin", + "2", + ] + `); }); it("for topicContainsPost", () => { const {url, posts, topic} = example(); @@ -229,15 +233,15 @@ describe("plugins/discourse/createGraph", () => { expect(edge.dst).toEqual(expectedDst); expect(edge.timestampMs).toEqual(post.timestampMs); expect(EdgeAddress.toParts(edge.address)).toMatchInlineSnapshot(` - Array [ - "sourcecred", - "discourse", - "topicContainsPost", - "https://url.com", - "1", - "2", - ] - `); + Array [ + "sourcecred", + "discourse", + "topicContainsPost", + "https://url.com", + "1", + "2", + ] + `); }); it("for postReplies", () => { const {url, posts, topic} = example(); @@ -249,13 +253,33 @@ describe("plugins/discourse/createGraph", () => { expect(edge.src).toEqual(expectedSrc); expect(edge.dst).toEqual(expectedDst); expect(edge.timestampMs).toEqual(post.timestampMs); + expect(EdgeAddress.toParts(edge.address)).toMatchInlineSnapshot(` + Array [ + "sourcecred", + "discourse", + "replyTo", + "https://url.com", + "3", + "2", + ] + `); + }); + it("for likes", () => { + const {url, likes} = example(); + const like = likes[0]; + const expectedSrc = userAddress(url, like.username); + const expectedDst = postAddress(url, like.postId); + const edge = likesEdge(url, like); + expect(edge.src).toEqual(expectedSrc); + expect(edge.dst).toEqual(expectedDst); + expect(edge.timestampMs).toEqual(like.timestampMs); expect(EdgeAddress.toParts(edge.address)).toMatchInlineSnapshot(` Array [ "sourcecred", "discourse", - "replyTo", + "likes", "https://url.com", - "3", + "mzargham", "2", ] `); @@ -322,5 +346,10 @@ describe("plugins/discourse/createGraph", () => { ]; expectEdgesOfType(edges, postRepliesEdgeType); }); + it("likes edges", () => { + const {url, likes} = example(); + const edges = likes.map((l) => likesEdge(url, l)); + expectEdgesOfType(edges, likesEdgeType); + }); }); }); diff --git a/src/plugins/discourse/declaration.js b/src/plugins/discourse/declaration.js index 3abf60a..d7cdc36 100644 --- a/src/plugins/discourse/declaration.js +++ b/src/plugins/discourse/declaration.js @@ -71,6 +71,14 @@ export const authorsPostEdgeType: EdgeType = deepFreeze({ description: "Connects an author to a post they've created.", }); +export const likesEdgeType: EdgeType = deepFreeze({ + forwardName: "likes", + backwardName: "is liked by", + prefix: EdgeAddress.fromParts(["sourcecred", "discourse", "likes"]), + defaultWeight: {forwards: 1, backwards: 0}, + description: "Connects a Discourse user to a post they liked.", +}); + export const declaration: PluginDeclaration = deepFreeze({ name: "discourse", nodeTypes: [userNodeType, topicNodeType, postNodeType], @@ -79,5 +87,6 @@ export const declaration: PluginDeclaration = deepFreeze({ authorsTopicEdgeType, authorsPostEdgeType, topicContainsPostEdgeType, + likesEdgeType, ], });