Discourse: add categoryId and bumpedMs to Topic data (#1454)

As not all API calls return bumpedMs, make a new type
to show the distinction.
This commit is contained in:
Robin van Boven 2019-11-16 13:52:32 +01:00 committed by GitHub
parent e79cca6c6c
commit 23f1db6ce4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 20 deletions

View File

@ -47,6 +47,7 @@ Object {
], ],
"topic": Object { "topic": Object {
"authorUsername": "d11", "authorUsername": "d11",
"categoryId": 1,
"id": 11, "id": 11,
"timestampMs": 1564744349408, "timestampMs": 1564744349408,
"title": "My First Test Post", "title": "My First Test Post",

View File

@ -84,6 +84,8 @@ describe("plugins/discourse/createGraph", () => {
title: "first topic", title: "first topic",
timestampMs: 0, timestampMs: 0,
authorUsername: "decentralion", authorUsername: "decentralion",
categoryId: 1,
bumpedMs: 0,
}; };
const post1 = { const post1 = {
id: 1, id: 1,

View File

@ -20,13 +20,29 @@ export type PostId = number;
export type TopicId = number; export type TopicId = number;
export type CategoryId = 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, +id: TopicId,
+categoryId: CategoryId,
+title: string, +title: string,
+timestampMs: number, +timestampMs: number,
+authorUsername: string, +authorUsername: string,
|}; |};
/**
* A complete Topic object.
*/
export type Topic = {|
...TopicView,
+bumpedMs: number,
|};
export type Post = {| export type Post = {|
+id: PostId, +id: PostId,
+topicId: TopicId, +topicId: TopicId,
@ -43,7 +59,7 @@ export type Post = {|
|}; |};
export type TopicWithPosts = {| export type TopicWithPosts = {|
+topic: Topic, +topic: TopicView,
// Not guaranteed to contain all the Posts in the topic—clients will need to // 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 // 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. // 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); failForNotOk(response);
const json = await response.json(); const json = await response.json();
const posts = json.post_stream.posts.map(parsePost); let posts = json.post_stream.posts.map(parsePost);
const topic: Topic = { const topic: TopicView = {
id: json.id, id: json.id,
categoryId: json.category_id,
title: json.title, title: json.title,
timestampMs: +new Date(json.created_at), timestampMs: Date.parse(json.created_at),
authorUsername: json.details.created_by.username, authorUsername: json.details.created_by.username,
}; };
return {topic, posts}; return {topic, posts};

View File

@ -1,7 +1,7 @@
// @flow // @flow
import type {TaskReporter} from "../../util/taskReporter"; import type {TaskReporter} from "../../util/taskReporter";
import type {Discourse, CategoryId} from "./fetch"; import type {Discourse, CategoryId, Topic} from "./fetch";
import {MirrorRepository} from "./mirrorRepository"; import {MirrorRepository} from "./mirrorRepository";
export type MirrorOptions = {| export type MirrorOptions = {|
@ -117,7 +117,10 @@ export class Mirror {
const topicWithPosts = await this._fetcher.topicWithPosts(topicId); const topicWithPosts = await this._fetcher.topicWithPosts(topicId);
if (topicWithPosts != null) { if (topicWithPosts != null) {
const {topic, posts} = topicWithPosts; 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) { for (const post of posts) {
addPost(post); addPost(post);
} }

View File

@ -9,6 +9,7 @@ import {
type TopicId, type TopicId,
type PostId, type PostId,
type Topic, type Topic,
type TopicView,
type Post, type Post,
type TopicWithPosts, type TopicWithPosts,
type LikeAction, 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 // 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 // test the functionality where we manually grab posts by ID
const posts = [firstPost]; const posts = [firstPost];
return {topic: this._topic(id), posts}; return {topic: this._topicView(id), posts};
} }
async likesByUser( async likesByUser(
@ -79,8 +80,16 @@ class MockFetcher implements Discourse {
} }
_topic(id: TopicId): Topic { _topic(id: TopicId): Topic {
return {
...this._topicView(id),
bumpedMs: 1000,
};
}
_topicView(id: TopicId): TopicView {
return { return {
id, id,
categoryId: 1,
title: `topic ${id}`, title: `topic ${id}`,
timestampMs: 1000, timestampMs: 1000,
authorUsername: "credbot", authorUsername: "credbot",

View File

@ -3,17 +3,11 @@
import type {Database} from "better-sqlite3"; import type {Database} from "better-sqlite3";
import stringify from "json-stable-stringify"; import stringify from "json-stable-stringify";
import dedent from "../../util/dedent"; import dedent from "../../util/dedent";
import { import type {TopicId, PostId, Topic, Post, LikeAction} from "./fetch";
type TopicId,
type PostId,
type Topic,
type Post,
type LikeAction,
} from "./fetch";
// 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_v4"; const VERSION = "discourse_mirror_v5";
/** /**
* An interface for reading the local Discourse data. * An interface for reading the local Discourse data.
@ -142,8 +136,10 @@ export class SqliteMirrorRepository
dedent`\ dedent`\
CREATE TABLE topics ( CREATE TABLE topics (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
category_id INTEGER NOT NULL,
title TEXT NOT NULL, title TEXT NOT NULL,
timestamp_ms INTEGER NOT NULL, timestamp_ms INTEGER NOT NULL,
bumped_ms INTEGER NOT NULL,
author_username TEXT NOT NULL, author_username TEXT NOT NULL,
FOREIGN KEY(author_username) REFERENCES users(username) FOREIGN KEY(author_username) REFERENCES users(username)
) )
@ -198,17 +194,21 @@ export class SqliteMirrorRepository
dedent`\ dedent`\
SELECT SELECT
id, id,
category_id,
title,
timestamp_ms, timestamp_ms,
author_username, bumped_ms,
title author_username
FROM topics` FROM topics`
) )
.all() .all()
.map((x) => ({ .map((x) => ({
id: x.id, id: x.id,
timestampMs: x.timestamp_ms, categoryId: x.category_id,
authorUsername: x.author_username,
title: x.title, title: x.title,
timestampMs: x.timestamp_ms,
bumpedMs: x.bumped_ms,
authorUsername: x.author_username,
})); }));
} }
@ -336,21 +336,27 @@ export class SqliteMirrorRepository
dedent`\ dedent`\
REPLACE INTO topics ( REPLACE INTO topics (
id, id,
category_id,
title, title,
timestamp_ms, timestamp_ms,
bumped_ms,
author_username author_username
) VALUES ( ) VALUES (
:id, :id,
:category_id,
:title, :title,
:timestamp_ms, :timestamp_ms,
:bumped_ms,
:author_username :author_username
) )
` `
) )
.run({ .run({
id: topic.id, id: topic.id,
category_id: topic.categoryId,
title: topic.title, title: topic.title,
timestamp_ms: topic.timestampMs, timestamp_ms: topic.timestampMs,
bumped_ms: topic.bumpedMs,
author_username: topic.authorUsername, author_username: topic.authorUsername,
}); });
return toAddResult(res); return toAddResult(res);