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:
parent
e79cca6c6c
commit
23f1db6ce4
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue