diff --git a/src/plugins/discourse/__snapshots__/fetch.test.js.snap b/src/plugins/discourse/__snapshots__/fetch.test.js.snap index dfbe108..62945be 100644 --- a/src/plugins/discourse/__snapshots__/fetch.test.js.snap +++ b/src/plugins/discourse/__snapshots__/fetch.test.js.snap @@ -47,6 +47,7 @@ Object { ], "topic": Object { "authorUsername": "d11", + "categoryId": 1, "id": 11, "timestampMs": 1564744349408, "title": "My First Test Post", diff --git a/src/plugins/discourse/createGraph.test.js b/src/plugins/discourse/createGraph.test.js index 213b777..4a4423b 100644 --- a/src/plugins/discourse/createGraph.test.js +++ b/src/plugins/discourse/createGraph.test.js @@ -84,6 +84,8 @@ describe("plugins/discourse/createGraph", () => { title: "first topic", timestampMs: 0, authorUsername: "decentralion", + categoryId: 1, + bumpedMs: 0, }; const post1 = { id: 1, diff --git a/src/plugins/discourse/fetch.js b/src/plugins/discourse/fetch.js index 6ac6969..ccb7ab9 100644 --- a/src/plugins/discourse/fetch.js +++ b/src/plugins/discourse/fetch.js @@ -20,13 +20,29 @@ export type PostId = number; export type TopicId = number; export type CategoryId = number; -export type Topic = {| +/** + * The "view" received from the Discourse API + * when getting a topic by ID. + * + * This filters some relevant data like bumpedMs, + * and the type separation makes this distinction clear. + */ +export type TopicView = {| +id: TopicId, + +categoryId: CategoryId, +title: string, +timestampMs: number, +authorUsername: string, |}; +/** + * A complete Topic object. + */ +export type Topic = {| + ...TopicView, + +bumpedMs: number, +|}; + export type Post = {| +id: PostId, +topicId: TopicId, @@ -43,7 +59,7 @@ export type Post = {| |}; export type TopicWithPosts = {| - +topic: Topic, + +topic: TopicView, // Not guaranteed to contain all the Posts in the topic—clients will need to // manually request some posts. The raw response actually includes a list of // all the PostIds in the topic, but for now we don't use them. @@ -182,11 +198,12 @@ export class Fetcher implements Discourse { } failForNotOk(response); const json = await response.json(); - const posts = json.post_stream.posts.map(parsePost); - const topic: Topic = { + let posts = json.post_stream.posts.map(parsePost); + const topic: TopicView = { id: json.id, + categoryId: json.category_id, title: json.title, - timestampMs: +new Date(json.created_at), + timestampMs: Date.parse(json.created_at), authorUsername: json.details.created_by.username, }; return {topic, posts}; diff --git a/src/plugins/discourse/mirror.js b/src/plugins/discourse/mirror.js index 663e4c8..d5ff83a 100644 --- a/src/plugins/discourse/mirror.js +++ b/src/plugins/discourse/mirror.js @@ -1,7 +1,7 @@ // @flow import type {TaskReporter} from "../../util/taskReporter"; -import type {Discourse, CategoryId} from "./fetch"; +import type {Discourse, CategoryId, Topic} from "./fetch"; import {MirrorRepository} from "./mirrorRepository"; export type MirrorOptions = {| @@ -117,7 +117,10 @@ export class Mirror { const topicWithPosts = await this._fetcher.topicWithPosts(topicId); if (topicWithPosts != null) { const {topic, posts} = topicWithPosts; - this._repo.addTopic(topic); + // TODO: Quick hack, as TopicView does not include bumpedMs. + // This should be resolved in the new sync logic. + const workaroundTopic: Topic = {...topic, bumpedMs: topic.timestampMs}; + this._repo.addTopic(workaroundTopic); for (const post of posts) { addPost(post); } diff --git a/src/plugins/discourse/mirror.test.js b/src/plugins/discourse/mirror.test.js index 64c2d0d..314253c 100644 --- a/src/plugins/discourse/mirror.test.js +++ b/src/plugins/discourse/mirror.test.js @@ -9,6 +9,7 @@ import { type TopicId, type PostId, type Topic, + type TopicView, type Post, type TopicWithPosts, type LikeAction, @@ -64,7 +65,7 @@ class MockFetcher implements Discourse { // Only return the first post in the posts array, to ensure that we have to // test the functionality where we manually grab posts by ID const posts = [firstPost]; - return {topic: this._topic(id), posts}; + return {topic: this._topicView(id), posts}; } async likesByUser( @@ -79,8 +80,16 @@ class MockFetcher implements Discourse { } _topic(id: TopicId): Topic { + return { + ...this._topicView(id), + bumpedMs: 1000, + }; + } + + _topicView(id: TopicId): TopicView { return { id, + categoryId: 1, title: `topic ${id}`, timestampMs: 1000, authorUsername: "credbot", diff --git a/src/plugins/discourse/mirrorRepository.js b/src/plugins/discourse/mirrorRepository.js index bc02c54..3aac391 100644 --- a/src/plugins/discourse/mirrorRepository.js +++ b/src/plugins/discourse/mirrorRepository.js @@ -3,17 +3,11 @@ import type {Database} from "better-sqlite3"; import stringify from "json-stable-stringify"; import dedent from "../../util/dedent"; -import { - type TopicId, - type PostId, - type Topic, - type Post, - type LikeAction, -} from "./fetch"; +import type {TopicId, PostId, Topic, Post, LikeAction} from "./fetch"; // The version should be bumped any time the database schema is changed, // so that the cache will be properly invalidated. -const VERSION = "discourse_mirror_v4"; +const VERSION = "discourse_mirror_v5"; /** * An interface for reading the local Discourse data. @@ -142,8 +136,10 @@ export class SqliteMirrorRepository dedent`\ CREATE TABLE topics ( id INTEGER PRIMARY KEY, + category_id INTEGER NOT NULL, title TEXT NOT NULL, timestamp_ms INTEGER NOT NULL, + bumped_ms INTEGER NOT NULL, author_username TEXT NOT NULL, FOREIGN KEY(author_username) REFERENCES users(username) ) @@ -198,17 +194,21 @@ export class SqliteMirrorRepository dedent`\ SELECT id, + category_id, + title, timestamp_ms, - author_username, - title + bumped_ms, + author_username FROM topics` ) .all() .map((x) => ({ id: x.id, - timestampMs: x.timestamp_ms, - authorUsername: x.author_username, + categoryId: x.category_id, title: x.title, + timestampMs: x.timestamp_ms, + bumpedMs: x.bumped_ms, + authorUsername: x.author_username, })); } @@ -336,21 +336,27 @@ export class SqliteMirrorRepository dedent`\ REPLACE INTO topics ( id, + category_id, title, timestamp_ms, + bumped_ms, author_username ) VALUES ( :id, + :category_id, :title, :timestamp_ms, + :bumped_ms, :author_username ) ` ) .run({ id: topic.id, + category_id: topic.categoryId, title: topic.title, timestamp_ms: topic.timestampMs, + bumped_ms: topic.bumpedMs, author_username: topic.authorUsername, }); return toAddResult(res);