@r/api-client
Advanced tools
| import apiRequest from '../apiBase/apiRequest'; | ||
| import SubredditRule from '../models2/SubredditRule'; | ||
| const ADD_RULE_PATH = 'api/add_subreddit_rule'; | ||
| const REMOVE_RULE_PATH = 'api/remove_subreddit_rule'; | ||
| const UPDATE_RULE_PATH = 'api/update_subreddit_rule'; | ||
| export default { | ||
| /** | ||
| * Get the rules for the given subreddit. | ||
| * Models are added to the APIResponse object, and can be retrieved using | ||
| * its `getModelFromRecord` method. E.g. | ||
| * | ||
| * const res = await SubredditRulesEndpoint.get(apiOptions, { id: 'beta' }); | ||
| * const rules = res.results.map(r => res.getModelFromRecord(r)); | ||
| * | ||
| * @function | ||
| * @param {Object} apiOptions | ||
| * @param {string} subredditName The name of a subreddit | ||
| * @returns {Promise<Object>} A promise resolving with the ApiResponse | ||
| */ | ||
| async get(apiOptions, subredditName) { | ||
| const path = `r/${subredditName}/about/rules.json`; | ||
| const query = { | ||
| raw_json: 1, | ||
| }; | ||
| const apiResponse = await apiRequest(apiOptions, 'GET', path, { query }); | ||
| const { rules } = apiResponse.response.body; | ||
| if (!(rules && rules.length)) { return apiResponse; } | ||
| rules.forEach(rule => { | ||
| // The SubredditRule model expects the rule to contain the name of the | ||
| // subreddit to which the rule belongs in order to make UUIDs. That is | ||
| // not actually returned from the API, though, so add it now. | ||
| rule.subredditName = subredditName; | ||
| apiResponse.addResult(SubredditRule.fromJSON(rule)) | ||
| }); | ||
| return apiResponse; | ||
| }, | ||
| /** | ||
| * Create a new subreddit rule. | ||
| * | ||
| * @function | ||
| * @param {Object} apiOptions | ||
| * @param {string} subredditName The name of a subreddit | ||
| * @param {Object} data | ||
| * @param {string} data.description Markdown formatted description of the rule | ||
| * @param {SubredditRule~RULE_TARGET} data.kind The types of things the rule applies to | ||
| * @param {string} data.shortName A short, plaintext title for the rule | ||
| * @param {?string} data.violationReason A short, plaintext string to use for reporting | ||
| * a violation of the rule. If omitted, the shortName will be used. | ||
| */ | ||
| async post(apiOptions, subredditName, data) { | ||
| const path = ADD_RULE_PATH; | ||
| const body = { | ||
| api_type: 'json', | ||
| raw_json: 1, | ||
| r: subredditName, | ||
| description: data.description, | ||
| kind: data.kind, | ||
| short_name: data.shortName, | ||
| }; | ||
| if (data.violationReason) { | ||
| body.violation_reason = data.violation_reason; | ||
| } | ||
| return apiRequest(apiOptions, 'POST', path, { body, type: 'form' }); | ||
| }, | ||
| /** | ||
| * Update a subreddit rule. | ||
| * @function | ||
| * @param {Object} apiOptions | ||
| * @param {string} subredditName The name of a subreddit | ||
| * @param {string} shortName The target rule's current shortName | ||
| * @param {Object} data | ||
| * @param {string} data.description Markdown formatted description of the rule | ||
| * @param {SubredditRule~RULE_TARGET} data.kind The types of things the rule applies to | ||
| * @param {string} data.shortName A short, plaintext title for the rule | ||
| */ | ||
| async put(apiOptions, subredditName, shortName, data) { | ||
| const path = UPDATE_RULE_PATH; | ||
| const body = { | ||
| api_type: 'json', | ||
| raw_json: 1, | ||
| r: subredditName, | ||
| old_short_name: shortName, | ||
| description: data.description, | ||
| kind: data.kind, | ||
| short_name: data.shortName, | ||
| }; | ||
| // Reddit's API will return the value of short_name if violation_reason doesn't exist. | ||
| // To support pulling down a rule, editing an unrelated field (e.g. description) and | ||
| // putting it back to the API w/o side effects, we should treat violationReason as empty | ||
| // if it is identical to shortName. It's necessary to use the old value of shortName | ||
| // here so that we don't keep it as the violationReason if only the shortName changes. | ||
| if (data.violationReason && data.violationReason !== shortName) { | ||
| data.violation_reason = data.violationReason; | ||
| } | ||
| return apiRequest(apiOptions, 'POST', path, { body, type: 'form' }); | ||
| }, | ||
| /** | ||
| * Delete a subreddit rule. | ||
| * @function | ||
| * @param {Object} apiOptions | ||
| * @param {string} subredditName The name of a subreddit | ||
| * @param {string} shortName The target rule's current shortName | ||
| */ | ||
| async del(apiOptions, subredditName, shortName) { | ||
| const path = REMOVE_RULE_PATH; | ||
| const body = { | ||
| api_type: 'json', | ||
| raw_json: 1, | ||
| r: subredditName, | ||
| short_name: shortName, | ||
| }; | ||
| return apiRequest(apiOptions, 'POST', path, { body, type: 'form' }); | ||
| }, | ||
| }; |
| import RedditModel from './RedditModel'; | ||
| import { SUBREDDIT_RULE } from './thingTypes'; | ||
| const T = RedditModel.Types; | ||
| export default class SubredditRule extends RedditModel { | ||
| static type = SUBREDDIT_RULE; | ||
| /** | ||
| * Valid types for rule targets. | ||
| * @enum | ||
| */ | ||
| static RULE_TARGET = { | ||
| ALL: 'all', | ||
| POST: 'link', | ||
| COMMENT: 'comment', | ||
| }; | ||
| static PROPERTIES = { | ||
| createdUTC: T.number, | ||
| description: T.string, | ||
| descriptionHTML: T.string, | ||
| kind: T.string, | ||
| priority: T.number, | ||
| shortName: T.string, | ||
| violationReason: T.string, | ||
| // The `subredditName` property is not returned from the API directly. It is | ||
| // mixed into the response data by `SubredditRulesEndpoint.get` in order | ||
| // to enable making unique UUIDs. | ||
| subredditName: T.string, | ||
| }; | ||
| static API_ALIASES = { | ||
| short_name: 'shortName', | ||
| created_utc: 'createdUTC', | ||
| description_html: 'descriptionHTML', | ||
| violation_reason: 'violationReason', | ||
| }; | ||
| makeUUID(data) { | ||
| // The actual rules model in r2 doesn't have a proper unique key, but | ||
| // the `created_utc` timestamp should work since it shouldn't change. | ||
| return `${data.subredditName}/${data.created_utc || data.createdUTC}`; | ||
| } | ||
| } |
+1
-1
| { | ||
| "name": "@r/api-client", | ||
| "version": "3.35.0", | ||
| "version": "3.36.0", | ||
| "description": "A wrapper for Reddit's API", | ||
@@ -5,0 +5,0 @@ "main": "build.js", |
@@ -12,2 +12,3 @@ import { forEach } from 'lodash/collection'; | ||
| SUBREDDIT, | ||
| SUBREDDIT_RULE, | ||
| WIKI, | ||
@@ -25,2 +26,3 @@ } from '../models2/thingTypes'; | ||
| this.subreddits = {}; | ||
| this.subreddit_rules = {}; | ||
| this.wikis = {}; | ||
@@ -34,2 +36,3 @@ | ||
| [SUBREDDIT]: this.subreddits, | ||
| [SUBREDDIT_RULE]: this.subreddit_rules, | ||
| [WIKI]: this.wikis, | ||
@@ -36,0 +39,0 @@ }; |
+4
-2
@@ -8,3 +8,2 @@ import { makeOptions, rawSend } from './apiBase/APIRequestUtils'; | ||
| // import reports from './apis/reports'; | ||
| // import rules from './apis/rules'; | ||
| // import stylesheets from './apis/stylesheets'; | ||
@@ -37,2 +36,3 @@ // import subredditRelationships from './apis/subredditRelationships'; | ||
| import SubredditsByPost from './apis/SubredditsByPost'; | ||
| import SubredditRulesEndpoint from './apis/SubredditRulesEndpoint'; | ||
| import SubredditsToPostsByPost from './apis/SubredditsToPostsByPost'; | ||
@@ -73,3 +73,2 @@ import WikisEndpoint from './apis/wikis'; | ||
| // reports, | ||
| // rules, | ||
| // stylesheets, | ||
@@ -96,2 +95,3 @@ // subredditRelationships, | ||
| SubredditsByPost, | ||
| SubredditRulesEndpoint, | ||
| SubredditsToPostsByPost, | ||
@@ -139,2 +139,3 @@ SubredditEndpoint, | ||
| import Subreddit from './models2/Subreddit'; | ||
| import SubredditRule from './models2/SubredditRule'; | ||
| import Wiki from './models2/Wiki'; | ||
@@ -178,2 +179,3 @@ | ||
| Subreddit, | ||
| SubredditRule, | ||
@@ -180,0 +182,0 @@ Wiki, |
@@ -27,2 +27,5 @@ export const COMMENT = 'comment'; | ||
| export const SUBREDDIT_RULE = 'subreddit_rule'; | ||
| export const SUBREDDIT_RULE_TYPE = 'subreddit_rule'; | ||
| const type_pairs = [ | ||
@@ -37,2 +40,3 @@ [COMMENT, COMMENT_TYPE], | ||
| [WIKI, WIKI_TYPE], | ||
| [SUBREDDIT_RULE, SUBREDDIT_RULE_TYPE], | ||
| ]; | ||
@@ -39,0 +43,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| import BaseContentEndpoint from './BaseContentEndpoint'; | ||
| import Rule from '../models/rule'; | ||
| import { has } from 'lodash/object'; | ||
| const SUBREDDIT_REGEX = /.*\/r\/(.+)\/about\/rules\.json.*/; | ||
| const ADD_RULE_PATH = 'api/add_subreddit_rule'; | ||
| const REMOVE_RULE_PATH = 'api/remove_subreddit_rule'; | ||
| const UPDATE_RULE_PATH = 'api/update_subreddit_rule'; | ||
| export default class RulesEndpoint extends BaseContentEndpoint { | ||
| model = Rule; | ||
| move = this.notImplemented('move'); | ||
| copy = this.notImplemented('copy'); | ||
| put = this.notImplemented('put'); | ||
| formatQuery(query, method) { | ||
| if (method !== 'get') { | ||
| query._method = 'post'; | ||
| } | ||
| query.raw_json = 1; | ||
| query.api_type = 'json'; | ||
| return query; | ||
| } | ||
| path(method, query={}) { | ||
| if (method === 'get') { | ||
| return `r/${query.subredditName}/about/rules.json`; | ||
| } else if (method === 'post') { | ||
| return ADD_RULE_PATH; | ||
| } else if (method === 'patch') { | ||
| return UPDATE_RULE_PATH; | ||
| } else if (method === 'del') { | ||
| return REMOVE_RULE_PATH; | ||
| } | ||
| } | ||
| post(data) { | ||
| const postData = { | ||
| api_type: 'json', | ||
| short_name: data.short_name, | ||
| description: data.description, | ||
| kind: data.kind || 'all', | ||
| r: data.subredditName || data.r, | ||
| }; | ||
| return super.post(postData); | ||
| } | ||
| patch(id, changes={}) { | ||
| const patchData = { | ||
| r: id.r || id.subredditName, | ||
| short_name: id.short_name, | ||
| }; | ||
| if (changes.short_name) { | ||
| patchData.old_short_name = patchData.short_name; | ||
| } | ||
| return super.patch({ | ||
| ...patchData, | ||
| ...changes, | ||
| }); | ||
| } | ||
| del(data) { | ||
| return super.del({ | ||
| api_type: 'json', | ||
| r: data.subredditName || data.r, | ||
| short_name: data.short_name, | ||
| }); | ||
| } | ||
| formatBody(res, req) { | ||
| const { body } = res; | ||
| if (req.method === 'GET') { | ||
| return this.formatGetBody(body, res, req); | ||
| } else if (req.url.indexOf(ADD_RULE_PATH) > -1) { | ||
| return this.formatAddRuleBody(body, res, req); | ||
| } else if (req.url.indexOf(UPDATE_RULE_PATH) > -1) { | ||
| return this.formatUpdateRuleBody(body, res, req); | ||
| } else if (req.url.indexOf(REMOVE_RULE_PATH) > -1) { | ||
| return this.formatRemoveRuleBody(body); | ||
| } | ||
| } | ||
| formatGetBody(body, res, req) { | ||
| let r; | ||
| const match = req.url.match(SUBREDDIT_REGEX); | ||
| if (match && match.length > 1) { | ||
| r = match[1]; | ||
| } | ||
| let subredditRules = []; | ||
| if (has(body, 'rules') && Array.isArray(body.rules)) { | ||
| subredditRules = body.rules.map(function(ruleData) { | ||
| return new Rule({ ...ruleData, r}).toJSON(); | ||
| }); | ||
| } | ||
| let siteRules = []; | ||
| if (has(body, 'site_rules') && Array.isArray(body.site_rules)) { | ||
| siteRules = body.site_rules; | ||
| } | ||
| return { | ||
| subredditRules, | ||
| siteRules, | ||
| }; | ||
| } | ||
| formatAddRuleBody(body, res, req) { | ||
| if (has(body, 'json.errors') && body.json.errors.length) { | ||
| throw body.json.errors; | ||
| } | ||
| // Have to pull the object back from the request.. | ||
| if (body.json) { | ||
| const ruleData = this.getRuleData(req._data); | ||
| if (has(body, 'json.data.description_html')) { | ||
| ruleData.description_html = body.json.data.description_html; | ||
| } | ||
| return new Rule(ruleData).toJSON(); | ||
| } | ||
| } | ||
| formatUpdateRuleBody(body, res, req) { | ||
| return { body, res, req }; | ||
| } | ||
| formatRemoveRuleBody(body) { | ||
| if (has(body, 'json.errors') && body.json.errors.length) { | ||
| throw body.json.errors; | ||
| } | ||
| } | ||
| getRuleData(requestData) { | ||
| return { | ||
| kind: requestData.kind || 'all', | ||
| description: requestData.description || '', | ||
| description_html: '', | ||
| short_name: requestData.short_name, | ||
| r: requestData.r, | ||
| }; | ||
| } | ||
| } |
| import Base from './base'; | ||
| export default class Rule extends Base { | ||
| _type = 'Rule'; | ||
| validators() { | ||
| const kind = this.kindValidator.bind(this); | ||
| const shortName = this.shortNameValidator.bind(this); | ||
| const r = this.subredditValidator.bind(this); | ||
| return { | ||
| kind, | ||
| short_name: shortName, | ||
| r, | ||
| }; | ||
| } | ||
| subredditValidator(subreddit) { | ||
| return Base.validators.string(subreddit) && | ||
| Base.validators.minLength(subreddit, 1); | ||
| } | ||
| shortNameValidator(shortName) { | ||
| return Base.validators.string(shortName) && | ||
| Base.validators.minLength(shortName, 1); | ||
| } | ||
| kindValidator(kind) { | ||
| return (['all', 'kind', 'comment'].indexOf(kind) > -1); | ||
| } | ||
| } |
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
4179
0.26%254986
-0.6%95
-3.06%24
9.09%