Skip to content

Commit

Permalink
Clean up raw sub config types; add note type APIs (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
eritbh authored Dec 19, 2023
1 parent dfdde69 commit 7b83797
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 87 deletions.
6 changes: 6 additions & 0 deletions src/classes/SubredditConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import test from 'ava';

test.todo('getAllNoteTypes');
test.todo('getNoteType');
test.todo('toJSON');
test.todo('toString');
82 changes: 82 additions & 0 deletions src/classes/SubredditConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
DEFAULT_USERNOTE_TYPES,
migrateConfigToLatestSchema,
} from '../helpers/config';
import {RawSubredditConfig, RawUsernoteType} from '../types/RawSubredditConfig';

// type imports for doc references
import type {Usernote} from '../types/Usernote';

/**
* A class that interfaces with the raw contents of a subreddit's `toolbox`
* wiki page, automatically handling schema checks and providing methods to read
* and modify subreddit configuration.
*/
export class SubredditConfig {
private data: RawSubredditConfig;

constructor (jsonString: string) {
this.data = migrateConfigToLatestSchema(JSON.parse(jsonString));
}

/** Returns all usernote types. */
getAllNoteTypes (): RawUsernoteType[] {
// If the config doesn't specify any note types, make a copy of the
// default set and add them to the config so the unambiguous form will
// be written back
if (!this.data.usernoteColors || !this.data.usernoteColors.length) {
const defaultTypes = DEFAULT_USERNOTE_TYPES.map(noteType => ({
...noteType,
}));
this.data.usernoteColors = defaultTypes;
}

return this.data.usernoteColors;
}

/**
* Returns the usernote type matching the given key. Useful for looking up
* display information for a usernote from {@linkcode Usernote.noteType}.
*
* @example Get the color and text of a note type from the key:
* ```ts
* const toolbox = new ToolboxClient(reddit);
* const subreddit = 'mildlyinteresting';
*
* // Acquire a note somehow
* const usernotes = toolbox.getUsernotes(subreddit);
* const note = usernotes.get('eritbh')[0];
*
* // Look up information about the type of this note
* const subConfig = toolbox.getConfig(subreddit);
* const {color, text} = subConfig.getNoteType(note.noteType);
* ```
*/
getNoteType (key: string) {
const noteTypes = this.getAllNoteTypes();
return noteTypes.find(noteType => noteType.key === key);
}

/**
* Serializes the subreddit config data for writing back to the wiki. **This
* method returns an object; you probably want {@linkcode toString}
* instead.**
* @returns Object which can be serialized to JSON and written as the
* contents of the `toolbox` wiki page
*/
toJSON () {
return this.data;
}

/**
* Stringifies the subreddit config data for writing back to the wiki.
* @param indent Passed as the third argument of `JSON.stringify`. Useful
* for debugging; however, because wiki space is limited, never provide this
* parameter when actually saving config to the wiki.
* @returns JSON string which can be saved as the contents of the `toolbox`
* wiki page
*/
toString (indent?: string | number) {
return JSON.stringify(this.data, null, indent);
}
}
10 changes: 10 additions & 0 deletions src/classes/ToolboxClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {RedditAPIClient} from '@devvit/public-api';
import {Usernote, UsernoteInit} from '../types/Usernote';
import {SubredditConfig} from './SubredditConfig';
import {Usernotes} from './Usernotes';

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

/** The name of the wiki page where Toolbox stores subreddit configuration. */
const TB_CONFIG_PAGE = 'toolbox';

/**
* A client class for interfacing with Toolbox functionality and stored data
* from within the Devvit platform. Wraps the Reddit API client provided in
Expand Down Expand Up @@ -135,4 +139,10 @@ export class ToolboxClient {
notes.add(note as Usernote);
await this.writeUsernotes(subreddit, notes, reason);
}

/** */
async getConfig (subreddit: string) {
const page = await this.reddit.getWikiPage(subreddit, TB_CONFIG_PAGE);
return new SubredditConfig(page.content);
}
}
3 changes: 3 additions & 0 deletions src/helpers/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import test from 'ava';

test.todo('migrateConfigToLatestSchema');
52 changes: 52 additions & 0 deletions src/helpers/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {RawSubredditConfig, RawUsernoteType} from '../types/RawSubredditConfig';

/**
* The latest subreddit config schema version that this library can handle. If a
* config page reports a schema version higher than this number, it can't be
* processed with this version of the library.
*/
export const LATEST_KNOWN_CONFIG_SCHEMA = 1;

/**
* The earliest subreddit config schema version that this library can handle. If
* a config page reports a schema version lower than this number, it can't be
* processed with this version of the library.
*/
export const EARLIEST_KNOWN_CONFIG_SCHEMA = 1;

/** Default usernote types used if subreddit config doesn't specify its own. */
export const DEFAULT_USERNOTE_TYPES: readonly RawUsernoteType[] = [
{key: 'gooduser', color: 'green', text: 'Good Contributor'},
{key: 'spamwatch', color: 'fuchsia', text: 'Spam Watch'},
{key: 'spamwarn', color: 'purple', text: 'Spam Warning'},
{key: 'abusewarn', color: 'orange', text: 'Abuse Warning'},
{key: 'ban', color: 'red', text: 'Ban'},
{key: 'permban', color: 'darkred', text: 'Permanent Ban'},
{key: 'botban', color: 'black', text: 'Bot Ban'},
];

/**
* Checks the schema version of raw subreddit config data and attempts to update
* it to the latest known schema version if it's out of date. Throws an error if
* the data's current schema version is too old or new to handle.
* @param data The subreddit config data object read from the wiki, as an object
* (i.e. you should parse the page contents as JSON to pass into this function)
* @returns Data object updated to latest schema version
*/
export function migrateConfigToLatestSchema (data: any): RawSubredditConfig {
if (data.ver < EARLIEST_KNOWN_CONFIG_SCHEMA) {
throw new TypeError(
`Unknown schema version ${data.ver} (earliest known version is ${EARLIEST_KNOWN_CONFIG_SCHEMA})`,
);
}
if (data.ver > LATEST_KNOWN_CONFIG_SCHEMA) {
throw new TypeError(
`Unknown schema version ${data.ver} (latest known version is ${LATEST_KNOWN_CONFIG_SCHEMA})`,
);
}

// In the future, if we ever do a schema bump to this page, migration steps
// will go here. See also migrateUsernotesToLatestSchema()

return data as RawSubredditConfig;
}
81 changes: 0 additions & 81 deletions src/subConfig.ts

This file was deleted.

Loading

0 comments on commit 7b83797

Please sign in to comment.