Skip to content

Commit 7b83797

Browse files
authored
Clean up raw sub config types; add note type APIs (#57)
1 parent dfdde69 commit 7b83797

9 files changed

+366
-87
lines changed

src/classes/SubredditConfig.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import test from 'ava';
2+
3+
test.todo('getAllNoteTypes');
4+
test.todo('getNoteType');
5+
test.todo('toJSON');
6+
test.todo('toString');

src/classes/SubredditConfig.ts

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {
2+
DEFAULT_USERNOTE_TYPES,
3+
migrateConfigToLatestSchema,
4+
} from '../helpers/config';
5+
import {RawSubredditConfig, RawUsernoteType} from '../types/RawSubredditConfig';
6+
7+
// type imports for doc references
8+
import type {Usernote} from '../types/Usernote';
9+
10+
/**
11+
* A class that interfaces with the raw contents of a subreddit's `toolbox`
12+
* wiki page, automatically handling schema checks and providing methods to read
13+
* and modify subreddit configuration.
14+
*/
15+
export class SubredditConfig {
16+
private data: RawSubredditConfig;
17+
18+
constructor (jsonString: string) {
19+
this.data = migrateConfigToLatestSchema(JSON.parse(jsonString));
20+
}
21+
22+
/** Returns all usernote types. */
23+
getAllNoteTypes (): RawUsernoteType[] {
24+
// If the config doesn't specify any note types, make a copy of the
25+
// default set and add them to the config so the unambiguous form will
26+
// be written back
27+
if (!this.data.usernoteColors || !this.data.usernoteColors.length) {
28+
const defaultTypes = DEFAULT_USERNOTE_TYPES.map(noteType => ({
29+
...noteType,
30+
}));
31+
this.data.usernoteColors = defaultTypes;
32+
}
33+
34+
return this.data.usernoteColors;
35+
}
36+
37+
/**
38+
* Returns the usernote type matching the given key. Useful for looking up
39+
* display information for a usernote from {@linkcode Usernote.noteType}.
40+
*
41+
* @example Get the color and text of a note type from the key:
42+
* ```ts
43+
* const toolbox = new ToolboxClient(reddit);
44+
* const subreddit = 'mildlyinteresting';
45+
*
46+
* // Acquire a note somehow
47+
* const usernotes = toolbox.getUsernotes(subreddit);
48+
* const note = usernotes.get('eritbh')[0];
49+
*
50+
* // Look up information about the type of this note
51+
* const subConfig = toolbox.getConfig(subreddit);
52+
* const {color, text} = subConfig.getNoteType(note.noteType);
53+
* ```
54+
*/
55+
getNoteType (key: string) {
56+
const noteTypes = this.getAllNoteTypes();
57+
return noteTypes.find(noteType => noteType.key === key);
58+
}
59+
60+
/**
61+
* Serializes the subreddit config data for writing back to the wiki. **This
62+
* method returns an object; you probably want {@linkcode toString}
63+
* instead.**
64+
* @returns Object which can be serialized to JSON and written as the
65+
* contents of the `toolbox` wiki page
66+
*/
67+
toJSON () {
68+
return this.data;
69+
}
70+
71+
/**
72+
* Stringifies the subreddit config data for writing back to the wiki.
73+
* @param indent Passed as the third argument of `JSON.stringify`. Useful
74+
* for debugging; however, because wiki space is limited, never provide this
75+
* parameter when actually saving config to the wiki.
76+
* @returns JSON string which can be saved as the contents of the `toolbox`
77+
* wiki page
78+
*/
79+
toString (indent?: string | number) {
80+
return JSON.stringify(this.data, null, indent);
81+
}
82+
}

src/classes/ToolboxClient.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import {RedditAPIClient} from '@devvit/public-api';
22
import {Usernote, UsernoteInit} from '../types/Usernote';
3+
import {SubredditConfig} from './SubredditConfig';
34
import {Usernotes} from './Usernotes';
45

56
/** The name of the wiki page where Toolbox stores usernotes. */
67
const TB_USERNOTES_PAGE = 'usernotes';
78

9+
/** The name of the wiki page where Toolbox stores subreddit configuration. */
10+
const TB_CONFIG_PAGE = 'toolbox';
11+
812
/**
913
* A client class for interfacing with Toolbox functionality and stored data
1014
* from within the Devvit platform. Wraps the Reddit API client provided in
@@ -135,4 +139,10 @@ export class ToolboxClient {
135139
notes.add(note as Usernote);
136140
await this.writeUsernotes(subreddit, notes, reason);
137141
}
142+
143+
/** */
144+
async getConfig (subreddit: string) {
145+
const page = await this.reddit.getWikiPage(subreddit, TB_CONFIG_PAGE);
146+
return new SubredditConfig(page.content);
147+
}
138148
}

src/helpers/config.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import test from 'ava';
2+
3+
test.todo('migrateConfigToLatestSchema');

src/helpers/config.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {RawSubredditConfig, RawUsernoteType} from '../types/RawSubredditConfig';
2+
3+
/**
4+
* The latest subreddit config schema version that this library can handle. If a
5+
* config page reports a schema version higher than this number, it can't be
6+
* processed with this version of the library.
7+
*/
8+
export const LATEST_KNOWN_CONFIG_SCHEMA = 1;
9+
10+
/**
11+
* The earliest subreddit config schema version that this library can handle. If
12+
* a config page reports a schema version lower than this number, it can't be
13+
* processed with this version of the library.
14+
*/
15+
export const EARLIEST_KNOWN_CONFIG_SCHEMA = 1;
16+
17+
/** Default usernote types used if subreddit config doesn't specify its own. */
18+
export const DEFAULT_USERNOTE_TYPES: readonly RawUsernoteType[] = [
19+
{key: 'gooduser', color: 'green', text: 'Good Contributor'},
20+
{key: 'spamwatch', color: 'fuchsia', text: 'Spam Watch'},
21+
{key: 'spamwarn', color: 'purple', text: 'Spam Warning'},
22+
{key: 'abusewarn', color: 'orange', text: 'Abuse Warning'},
23+
{key: 'ban', color: 'red', text: 'Ban'},
24+
{key: 'permban', color: 'darkred', text: 'Permanent Ban'},
25+
{key: 'botban', color: 'black', text: 'Bot Ban'},
26+
];
27+
28+
/**
29+
* Checks the schema version of raw subreddit config data and attempts to update
30+
* it to the latest known schema version if it's out of date. Throws an error if
31+
* the data's current schema version is too old or new to handle.
32+
* @param data The subreddit config data object read from the wiki, as an object
33+
* (i.e. you should parse the page contents as JSON to pass into this function)
34+
* @returns Data object updated to latest schema version
35+
*/
36+
export function migrateConfigToLatestSchema (data: any): RawSubredditConfig {
37+
if (data.ver < EARLIEST_KNOWN_CONFIG_SCHEMA) {
38+
throw new TypeError(
39+
`Unknown schema version ${data.ver} (earliest known version is ${EARLIEST_KNOWN_CONFIG_SCHEMA})`,
40+
);
41+
}
42+
if (data.ver > LATEST_KNOWN_CONFIG_SCHEMA) {
43+
throw new TypeError(
44+
`Unknown schema version ${data.ver} (latest known version is ${LATEST_KNOWN_CONFIG_SCHEMA})`,
45+
);
46+
}
47+
48+
// In the future, if we ever do a schema bump to this page, migration steps
49+
// will go here. See also migrateUsernotesToLatestSchema()
50+
51+
return data as RawSubredditConfig;
52+
}

src/subConfig.ts

-81
This file was deleted.

0 commit comments

Comments
 (0)