mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-27 03:00:12 -04:00
Add PATCH /api/v1/me/prefs endpoint
Allows partial update of the user's preferences
This commit is contained in:
@@ -19,14 +19,18 @@
|
||||
# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit
|
||||
# Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
import json
|
||||
|
||||
from pylons import c
|
||||
from r2.controllers.api_docs import api_doc, api_section
|
||||
from r2.controllers.oauth2 import require_oauth2_scope
|
||||
from r2.controllers.reddit_base import OAuth2ResourceController
|
||||
from r2.controllers.reddit_base import (
|
||||
abort_with_error,
|
||||
OAuth2ResourceController,
|
||||
)
|
||||
from r2.lib.base import abort
|
||||
from r2.lib.jsontemplates import IdentityJsonTemplate, PrefsJsonTemplate
|
||||
from r2.lib.validator import (
|
||||
nop,
|
||||
validate,
|
||||
VContentLang,
|
||||
VList,
|
||||
@@ -44,7 +48,7 @@ PREFS_JSON_SPEC = VValidatedJSON.PartialObject({
|
||||
})
|
||||
|
||||
PREFS_JSON_SPEC.spec["content_langs"] = VValidatedJSON.ArrayOf(
|
||||
VContentLang("content_langs")
|
||||
VContentLang("content_langs")
|
||||
)
|
||||
|
||||
|
||||
@@ -81,3 +85,21 @@ class APIv1Controller(OAuth2ResourceController):
|
||||
"""Return the preference settings of the logged in user"""
|
||||
resp = PrefsJsonTemplate(fields).data(c.oauth_user)
|
||||
return self.api_wrapper(resp)
|
||||
|
||||
PREFS_JSON_VALIDATOR = VValidatedJSON("json", PREFS_JSON_SPEC,
|
||||
body=True)
|
||||
|
||||
@require_oauth2_scope("account")
|
||||
@api_doc(api_section.account, json_model=PREFS_JSON_VALIDATOR)
|
||||
@validate(validated_prefs=PREFS_JSON_VALIDATOR)
|
||||
def PATCH_prefs(self, validated_prefs):
|
||||
user_prefs = c.user.preferences()
|
||||
for short_name, new_value in validated_prefs.iteritems():
|
||||
pref_name = "pref_" + short_name
|
||||
if pref_name == "pref_content_langs":
|
||||
new_value = vprefs.format_content_lang_pref(new_value)
|
||||
user_prefs[pref_name] = new_value
|
||||
vprefs.filter_prefs(user_prefs, c.user)
|
||||
vprefs.set_prefs(c.user, user_prefs)
|
||||
c.user._commit()
|
||||
return self.api_wrapper(PrefsJsonTemplate().data(c.user))
|
||||
|
||||
@@ -208,7 +208,11 @@ class ErrorController(RedditController):
|
||||
except Exception as e:
|
||||
return handle_awful_failure("ErrorController.GET_document: %r" % e)
|
||||
|
||||
POST_document = PUT_document = DELETE_document = GET_document
|
||||
POST_document = GET_document
|
||||
PUT_document = GET_document
|
||||
PATCH_document = GET_document
|
||||
DELETE_document = GET_document
|
||||
|
||||
|
||||
def handle_awful_failure(fail_text):
|
||||
"""
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit
|
||||
# Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
from r2.lib.pages import *
|
||||
from reddit_base import cross_domain
|
||||
from api import ApiController
|
||||
@@ -50,7 +49,6 @@ class PostController(ApiController):
|
||||
langs.append(str(lang))
|
||||
return format_content_lang_pref(langs)
|
||||
|
||||
|
||||
@validate(pref_lang = VLang('lang'),
|
||||
all_langs = VOneOf('all-langs', ('all', 'some'), default='all'))
|
||||
def POST_unlogged_options(self, all_langs, pref_lang):
|
||||
|
||||
@@ -724,12 +724,12 @@ def flatten_response(content):
|
||||
return "".join(_force_utf8(x) for x in tup(content) if x)
|
||||
|
||||
|
||||
def abort_with_error(error):
|
||||
if not error.code:
|
||||
def abort_with_error(error, code=None):
|
||||
if not code and not error.code:
|
||||
raise ValueError('Error %r missing status code' % error)
|
||||
|
||||
abort(reddit_http_error(
|
||||
code=error.code,
|
||||
code=code or error.code,
|
||||
error_name=error.name,
|
||||
explanation=error.message,
|
||||
fields=error.fields,
|
||||
|
||||
@@ -78,7 +78,7 @@ def can_comment_link(article):
|
||||
class Validator(object):
|
||||
default_param = None
|
||||
def __init__(self, param=None, default=None, post=True, get=True, url=True,
|
||||
docs=None):
|
||||
body=False, docs=None):
|
||||
if param:
|
||||
self.param = param
|
||||
else:
|
||||
@@ -86,6 +86,7 @@ class Validator(object):
|
||||
|
||||
self.default = default
|
||||
self.post, self.get, self.url, self.docs = post, get, url, docs
|
||||
self.body = body
|
||||
self.has_errors = False
|
||||
|
||||
def set_error(self, error, msg_params={}, field=False, code=None):
|
||||
@@ -120,6 +121,8 @@ class Validator(object):
|
||||
val = request.GET[p]
|
||||
elif self.url and url.get(p):
|
||||
val = url[p]
|
||||
elif self.body:
|
||||
val = request.body
|
||||
else:
|
||||
val = self.default
|
||||
a.append(val)
|
||||
@@ -371,6 +374,17 @@ class VLang(Validator):
|
||||
self.param: "a valid IETF language tag (underscore separated)",
|
||||
}
|
||||
|
||||
|
||||
class VContentLang(VLang):
|
||||
def run(self, lang):
|
||||
if lang == "all":
|
||||
return lang
|
||||
try:
|
||||
return VLang.validate_lang(lang, strict=True)
|
||||
except ValueError:
|
||||
self.set_error(errors.INVALID_LANG)
|
||||
|
||||
|
||||
class VRequired(Validator):
|
||||
def __init__(self, param, error, *a, **kw):
|
||||
Validator.__init__(self, param, *a, **kw)
|
||||
@@ -1436,6 +1450,9 @@ class VUserWithEmail(VExistingUname):
|
||||
|
||||
class VBoolean(Validator):
|
||||
def run(self, val):
|
||||
if val is True or val is False:
|
||||
# val is already a bool object, no processing needed
|
||||
return val
|
||||
lv = str(val).lower()
|
||||
if lv == 'off' or lv == '' or lv[0] in ("f", "n"):
|
||||
return False
|
||||
@@ -2436,7 +2453,7 @@ class VValidatedJSON(VJSON):
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
def run(self, data):
|
||||
def run(self, data, ignore_missing=False):
|
||||
if not isinstance(data, dict):
|
||||
raise RedditError('JSON_INVALID', code=400)
|
||||
|
||||
@@ -2445,6 +2462,8 @@ class VValidatedJSON(VJSON):
|
||||
try:
|
||||
validated_data[key] = validator.run(data[key])
|
||||
except KeyError:
|
||||
if ignore_missing:
|
||||
continue
|
||||
raise RedditError('JSON_MISSING_KEY', code=400,
|
||||
msg_params={'key': key})
|
||||
return validated_data
|
||||
@@ -2470,6 +2489,10 @@ class VValidatedJSON(VJSON):
|
||||
spec_lines.append('}')
|
||||
return '\n'.join(spec_lines)
|
||||
|
||||
class PartialObject(Object):
|
||||
def run(self, data):
|
||||
super_ = super(VValidatedJSON.PartialObject, self)
|
||||
return super_.run(data, ignore_missing=True)
|
||||
|
||||
def __init__(self, param, spec, **kw):
|
||||
VJSON.__init__(self, param, **kw)
|
||||
|
||||
@@ -123,7 +123,11 @@ class Account(Thing):
|
||||
state=0,
|
||||
modmsgtime=None,
|
||||
)
|
||||
_preference_attrs = (k for k in _defaults.keys() if k.startswith("pref_"))
|
||||
_preference_attrs = tuple(k for k in _defaults.keys()
|
||||
if k.startswith("pref_"))
|
||||
|
||||
def preferences(self):
|
||||
return {pref: getattr(self, pref) for pref in self._preference_attrs}
|
||||
|
||||
def __eq__(self, other):
|
||||
if type(self) != type(other):
|
||||
|
||||
@@ -101,6 +101,13 @@ class ConsumableToken(Token):
|
||||
|
||||
class OAuth2Scope:
|
||||
scope_info = {
|
||||
"account": {
|
||||
"id": "account",
|
||||
"name": _("Update account information"),
|
||||
"description": _("Update preferences and related account "
|
||||
"information. Will not have access to your email or "
|
||||
"password."),
|
||||
},
|
||||
"edit": {
|
||||
"id": "edit",
|
||||
"name": _("Edit Posts"),
|
||||
|
||||
Reference in New Issue
Block a user