refactor discourse createGraph (#1409)

This is a minor refactor to re-organize the createGraph function in the
Discourse plugin to use a class under the hood. Using a hidden class
makes sense because there is a fair bit of shared state that's needed
while creating the graph.

The proximate cause for this refactor is tha adding reference edges will
bloat the `addPost` section of the function, which was already a little
too complex. Simply shoving in more complexity would make it unweidy. So
I opted for this minor refactor. It's internal-only (no public APIs are
changed).

Test plan: `yarn test` passes. As noted, refactor is internal-only.

This is progress towards [Discourse reference and mention detection][1].

[1]: https://discourse.sourcecred.io/t/discourse-reference-mention-detection/270
This commit is contained in:
Dandelion Mané 2019-10-11 13:46:49 -06:00 committed by GitHub
parent d4804a7a68
commit e1a73ac368
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 53 additions and 24 deletions

View File

@ -128,27 +128,59 @@ export function likesEdge(serverUrl: string, like: LikeAction): Edge {
} }
export function createGraph(serverUrl: string, data: DiscourseData): Graph { export function createGraph(serverUrl: string, data: DiscourseData): Graph {
const gc = new _GraphCreator(serverUrl, data);
return gc.graph;
}
class _GraphCreator {
graph: Graph;
serverUrl: string;
data: DiscourseData;
topicIdToTitle: Map<TopicId, string>;
constructor(serverUrl: string, data: DiscourseData) {
if (serverUrl.endsWith("/")) { if (serverUrl.endsWith("/")) {
throw new Error(`by convention, serverUrl should not end with /`); throw new Error(`by convention, serverUrl should not end with /`);
} }
const g = new Graph(); this.serverUrl = serverUrl;
const topicIdToTitle: Map<TopicId, string> = new Map(); this.data = data;
this.graph = new Graph();
this.topicIdToTitle = new Map();
for (const username of data.users()) { for (const username of data.users()) {
g.addNode(userNode(serverUrl, username)); this.graph.addNode(userNode(serverUrl, username));
} }
for (const topic of data.topics()) { for (const topic of data.topics()) {
topicIdToTitle.set(topic.id, topic.title); this.topicIdToTitle.set(topic.id, topic.title);
g.addNode(topicNode(serverUrl, topic)); this.graph.addNode(topicNode(serverUrl, topic));
g.addEdge(authorsTopicEdge(serverUrl, topic)); this.graph.addEdge(authorsTopicEdge(serverUrl, topic));
} }
for (const post of data.posts()) { for (const post of data.posts()) {
const topicTitle = topicIdToTitle.get(post.topicId) || "[unknown topic]"; this.addPost(post);
g.addNode(postNode(serverUrl, post, topicTitle)); }
g.addEdge(authorsPostEdge(serverUrl, post));
g.addEdge(topicContainsPostEdge(serverUrl, post)); for (const like of data.likes()) {
this.graph.addEdge(likesEdge(serverUrl, like));
}
}
addPost(post: Post) {
const topicTitle =
this.topicIdToTitle.get(post.topicId) || "[unknown topic]";
this.graph.addNode(postNode(this.serverUrl, post, topicTitle));
this.graph.addEdge(authorsPostEdge(this.serverUrl, post));
this.graph.addEdge(topicContainsPostEdge(this.serverUrl, post));
this.maybeAddPostRepliesEdge(post);
}
/**
* Any post that is not the first post in the thread is a reply to some post.
* This method adds those reply edges. It is a bit hairy to work around unintuitive
* choices in the Discourse API.
*/
maybeAddPostRepliesEdge(post: Post) {
let replyToPostIndex = post.replyToPostIndex; let replyToPostIndex = post.replyToPostIndex;
if (replyToPostIndex == null && post.indexWithinTopic > 1) { if (replyToPostIndex == null && post.indexWithinTopic > 1) {
// For posts that are a reply to the first posts (or, depending on how you look at it, // For posts that are a reply to the first posts (or, depending on how you look at it,
@ -158,16 +190,13 @@ export function createGraph(serverUrl: string, data: DiscourseData): Graph {
replyToPostIndex = 1; replyToPostIndex = 1;
} }
if (replyToPostIndex != null) { if (replyToPostIndex != null) {
const basePostId = data.findPostInTopic(post.topicId, replyToPostIndex); const basePostId = this.data.findPostInTopic(
post.topicId,
replyToPostIndex
);
if (basePostId != null) { if (basePostId != null) {
g.addEdge(postRepliesEdge(serverUrl, post, basePostId)); this.graph.addEdge(postRepliesEdge(this.serverUrl, post, basePostId));
} }
} }
} }
for (const like of data.likes()) {
g.addEdge(likesEdge(serverUrl, like));
}
return g;
} }