mirror: allow fetching all usernames (#1293)
This is a minor change to the Discourse mirror so that it supports a query to get all users from the server. It will be convenient for a followon change which makes `update` search for every user's likes. I also modified createGraph so that it uses the new method, which results in code that is cleaner and slightly more efficient. Test plan: Unit tests updated.
This commit is contained in:
parent
f3ae0a8415
commit
e7b1fbd681
|
@ -130,17 +130,19 @@ export function createGraph(serverUrl: string, data: DiscourseData): Graph {
|
||||||
const g = new Graph();
|
const g = new Graph();
|
||||||
const topicIdToTitle: Map<TopicId, string> = new Map();
|
const topicIdToTitle: Map<TopicId, string> = new Map();
|
||||||
|
|
||||||
|
for (const username of data.users()) {
|
||||||
|
g.addNode(userNode(serverUrl, username));
|
||||||
|
}
|
||||||
|
|
||||||
for (const topic of data.topics()) {
|
for (const topic of data.topics()) {
|
||||||
topicIdToTitle.set(topic.id, topic.title);
|
topicIdToTitle.set(topic.id, topic.title);
|
||||||
g.addNode(topicNode(serverUrl, topic));
|
g.addNode(topicNode(serverUrl, topic));
|
||||||
g.addNode(userNode(serverUrl, topic.authorUsername));
|
|
||||||
g.addEdge(authorsTopicEdge(serverUrl, topic));
|
g.addEdge(authorsTopicEdge(serverUrl, topic));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const post of data.posts()) {
|
for (const post of data.posts()) {
|
||||||
const topicTitle = topicIdToTitle.get(post.topicId) || "[unknown topic]";
|
const topicTitle = topicIdToTitle.get(post.topicId) || "[unknown topic]";
|
||||||
g.addNode(postNode(serverUrl, post, topicTitle));
|
g.addNode(postNode(serverUrl, post, topicTitle));
|
||||||
g.addNode(userNode(serverUrl, post.authorUsername));
|
|
||||||
g.addEdge(authorsPostEdge(serverUrl, post));
|
g.addEdge(authorsPostEdge(serverUrl, post));
|
||||||
g.addEdge(topicContainsPostEdge(serverUrl, post));
|
g.addEdge(topicContainsPostEdge(serverUrl, post));
|
||||||
let replyToPostIndex = post.replyToPostIndex;
|
let replyToPostIndex = post.replyToPostIndex;
|
||||||
|
|
|
@ -40,6 +40,16 @@ describe("plugins/discourse/createGraph", () => {
|
||||||
posts(): $ReadOnlyArray<Post> {
|
posts(): $ReadOnlyArray<Post> {
|
||||||
return this._posts;
|
return this._posts;
|
||||||
}
|
}
|
||||||
|
users(): $ReadOnlyArray<string> {
|
||||||
|
const users = new Set();
|
||||||
|
for (const {authorUsername} of this.posts()) {
|
||||||
|
users.add(authorUsername);
|
||||||
|
}
|
||||||
|
for (const {authorUsername} of this.topics()) {
|
||||||
|
users.add(authorUsername);
|
||||||
|
}
|
||||||
|
return Array.from(users);
|
||||||
|
}
|
||||||
findPostInTopic(topicId: TopicId, indexWithinTopic: number): ?PostId {
|
findPostInTopic(topicId: TopicId, indexWithinTopic: number): ?PostId {
|
||||||
const post = this._posts.filter(
|
const post = this._posts.filter(
|
||||||
(p) => p.topicId === topicId && p.indexWithinTopic === indexWithinTopic
|
(p) => p.topicId === topicId && p.indexWithinTopic === indexWithinTopic
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
|
|
||||||
// The version should be bumped any time the database schema is changed,
|
// The version should be bumped any time the database schema is changed,
|
||||||
// so that the cache will be properly invalidated.
|
// so that the cache will be properly invalidated.
|
||||||
const VERSION = "discourse_mirror_v1";
|
const VERSION = "discourse_mirror_v2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for retrieving all of the Discourse data at once.
|
* An interface for retrieving all of the Discourse data at once.
|
||||||
|
@ -45,6 +45,13 @@ export interface DiscourseData {
|
||||||
* Returns undefined if no such post is available.
|
* Returns undefined if no such post is available.
|
||||||
*/
|
*/
|
||||||
findPostInTopic(topicId: TopicId, indexWithinTopic: number): ?PostId;
|
findPostInTopic(topicId: TopicId, indexWithinTopic: number): ?PostId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get usernames for all users.
|
||||||
|
*
|
||||||
|
* The order is unspecified.
|
||||||
|
*/
|
||||||
|
users(): $ReadOnlyArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -131,12 +138,14 @@ export class Mirror implements DiscourseData {
|
||||||
db.prepare("INSERT INTO meta (zero, config) VALUES (0, ?)").run(config);
|
db.prepare("INSERT INTO meta (zero, config) VALUES (0, ?)").run(config);
|
||||||
|
|
||||||
const tables = [
|
const tables = [
|
||||||
|
"CREATE TABLE users (username TEXT PRIMARY KEY)",
|
||||||
dedent`\
|
dedent`\
|
||||||
CREATE TABLE topics (
|
CREATE TABLE topics (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
timestamp_ms INTEGER NOT NULL,
|
timestamp_ms INTEGER NOT NULL,
|
||||||
author_username TEXT NOT NULL
|
author_username TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(author_username) REFERENCES users(username)
|
||||||
)
|
)
|
||||||
`,
|
`,
|
||||||
dedent`\
|
dedent`\
|
||||||
|
@ -147,7 +156,8 @@ export class Mirror implements DiscourseData {
|
||||||
topic_id INTEGER NOT NULL,
|
topic_id INTEGER NOT NULL,
|
||||||
index_within_topic INTEGER NOT NULL,
|
index_within_topic INTEGER NOT NULL,
|
||||||
reply_to_post_index INTEGER,
|
reply_to_post_index INTEGER,
|
||||||
FOREIGN KEY(topic_id) REFERENCES topics(id)
|
FOREIGN KEY(topic_id) REFERENCES topics(id),
|
||||||
|
FOREIGN KEY(author_username) REFERENCES users(username)
|
||||||
)
|
)
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -200,6 +210,13 @@ export class Mirror implements DiscourseData {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
users(): $ReadOnlyArray<string> {
|
||||||
|
return this._db
|
||||||
|
.prepare("SELECT username FROM users")
|
||||||
|
.pluck()
|
||||||
|
.all();
|
||||||
|
}
|
||||||
|
|
||||||
findPostInTopic(topicId: TopicId, indexWithinTopic: number): ?PostId {
|
findPostInTopic(topicId: TopicId, indexWithinTopic: number): ?PostId {
|
||||||
return this._db
|
return this._db
|
||||||
.prepare(
|
.prepare(
|
||||||
|
@ -237,6 +254,9 @@ export class Mirror implements DiscourseData {
|
||||||
topicId,
|
topicId,
|
||||||
authorUsername,
|
authorUsername,
|
||||||
} = post;
|
} = post;
|
||||||
|
db.prepare("INSERT OR IGNORE INTO users (username) VALUES (?)").run(
|
||||||
|
authorUsername
|
||||||
|
);
|
||||||
db.prepare(
|
db.prepare(
|
||||||
dedent`\
|
dedent`\
|
||||||
REPLACE INTO posts (
|
REPLACE INTO posts (
|
||||||
|
@ -275,6 +295,9 @@ export class Mirror implements DiscourseData {
|
||||||
if (topicWithPosts != null) {
|
if (topicWithPosts != null) {
|
||||||
const {topic, posts} = topicWithPosts;
|
const {topic, posts} = topicWithPosts;
|
||||||
const {id, title, timestampMs, authorUsername} = topic;
|
const {id, title, timestampMs, authorUsername} = topic;
|
||||||
|
db.prepare("INSERT OR IGNORE INTO users (username) VALUES (?)").run(
|
||||||
|
authorUsername
|
||||||
|
);
|
||||||
this._db
|
this._db
|
||||||
.prepare(
|
.prepare(
|
||||||
dedent`\
|
dedent`\
|
||||||
|
|
|
@ -194,6 +194,21 @@ describe("plugins/discourse/mirror", () => {
|
||||||
expect(mirror.posts()).toEqual(posts);
|
expect(mirror.posts()).toEqual(posts);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("provides usernames for all active users", async () => {
|
||||||
|
const {mirror, fetcher} = example();
|
||||||
|
fetcher.addPost(2, null, "alpha");
|
||||||
|
fetcher.addPost(3, null, "beta");
|
||||||
|
fetcher.addPost(3, 1, "alpha");
|
||||||
|
await mirror.update();
|
||||||
|
// credbot appears because it is the nominal author of all topics
|
||||||
|
expect(
|
||||||
|
mirror
|
||||||
|
.users()
|
||||||
|
.slice()
|
||||||
|
.sort()
|
||||||
|
).toEqual(["alpha", "beta", "credbot"]);
|
||||||
|
});
|
||||||
|
|
||||||
describe("update semantics", () => {
|
describe("update semantics", () => {
|
||||||
it("only fetches new topics on `update`", async () => {
|
it("only fetches new topics on `update`", async () => {
|
||||||
const {mirror, fetcher} = example();
|
const {mirror, fetcher} = example();
|
||||||
|
|
Loading…
Reference in New Issue