refine message object schema; update docstrings

pull/4/head
Blake DeMarcy 2017-04-02 23:23:19 -05:00
parent 73a9063f93
commit af5b53e7e3
2 changed files with 25 additions and 51 deletions

View File

@ -30,8 +30,7 @@ def api_method(function):
""" """
A wrapper that handles encoding of objects and errors to a A wrapper that handles encoding of objects and errors to a
standard format for the API, resolves and authorizes users standard format for the API, resolves and authorizes users
from header data, and prepares cherrypy.thread_data so other from header data, and prepares the arguments for each method.
funtions can handle the request.
In the body of each api method and all the functions In the body of each api method and all the functions
they utilize, BBJExceptions are caught and their attached they utilize, BBJExceptions are caught and their attached
@ -40,8 +39,9 @@ def api_method(function):
it for inspection. Errors related to JSON decoding are it for inspection. Errors related to JSON decoding are
caught as well and returned to the client as code 0. caught as well and returned to the client as code 0.
""" """
function.exposed = True
@wraps(function) @wraps(function)
def wrapper(*args, **kwargs): def wrapper(self, *args, **kwargs):
response = None response = None
try: try:
# read in the body from the request to a string... # read in the body from the request to a string...
@ -50,11 +50,8 @@ def api_method(function):
if body: if body:
body = json.loads(body) body = json.loads(body)
if isinstance(body, dict): if isinstance(body, dict):
# lowercase all of its keys # lowercase all of its top-level keys
body = {str(key).lower(): value for key, value body = {str(key).lower(): value for key, value in body.items()}
in body.items()}
else: # would rather a NoneType than b""
body = None
username = cherrypy.request.headers.get("User") username = cherrypy.request.headers.get("User")
auth = cherrypy.request.headers.get("Auth") auth = cherrypy.request.headers.get("Auth")
@ -78,8 +75,7 @@ def api_method(function):
# api_methods may choose to bind a usermap into the thread_data # api_methods may choose to bind a usermap into the thread_data
# which will send it off with the response # which will send it off with the response
cherrypy.thread_data.usermap = {} cherrypy.thread_data.usermap = {}
# TODO: Why in kek's name is self needing to be supplied a value positionally? value = function(self, body, cherrypy.thread_data.db, user)
value = function(None, body, cherrypy.thread_data.db, user)
response = schema.response(value, cherrypy.thread_data.usermap) response = schema.response(value, cherrypy.thread_data.usermap)
except BBJException as e: except BBJException as e:
@ -91,7 +87,7 @@ def api_method(function):
except Exception as e: except Exception as e:
error_id = uuid1().hex error_id = uuid1().hex
response = schema.error(1, response = schema.error(1,
"Internal server error: code {}. {}" "Internal server error: code {} {}"
.format(error_id, repr(e))) .format(error_id, repr(e)))
with open("logs/exceptions/" + error_id, "a") as log: with open("logs/exceptions/" + error_id, "a") as log:
traceback.print_tb(e.__traceback__, file=log) traceback.print_tb(e.__traceback__, file=log)
@ -111,10 +107,6 @@ def create_usermap(connection, obj):
be a thread_index or a messages object from one. be a thread_index or a messages object from one.
""" """
if isinstance(obj, dict):
# this is a message object for a thread, ditch the keys
obj = obj.values()
return { return {
user_id: db.user_resolve( user_id: db.user_resolve(
connection, connection,
@ -144,22 +136,19 @@ def validate(json, args):
.format(arg, ", ".join(args))) .format(arg, ", ".join(args)))
APICONFIG = {
"/": {
"tools.response_headers.on": True,
"tools.response_headers.headers": [
("Content-Type", "application/json")
],
}
}
class API(object): class API(object):
"""
This object contains all the API endpoints for bbj.
The html serving part of the server is not written
yet, so this is currently the only module being
served.
"""
@api_method @api_method
@cherrypy.expose
def user_register(self, args, database, user, **kwargs): def user_register(self, args, database, user, **kwargs):
""" """
Register a new user into the system and return the new object. Register a new user into the system and return the new object.
Requires the string arguments `user_name` and `auth_hash` Requires the string arguments `user_name` and `auth_hash`.
Do not send User/Auth headers with this method.
""" """
validate(args, ["user_name", "auth_hash"]) validate(args, ["user_name", "auth_hash"])
return db.user_register( return db.user_register(
@ -167,7 +156,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def user_update(self, args, database, user, **kwargs): def user_update(self, args, database, user, **kwargs):
""" """
Receives new parameters and assigns them to the user_object Receives new parameters and assigns them to the user_object
@ -182,7 +170,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def get_me(self, args, database, user, **kwargs): def get_me(self, args, database, user, **kwargs):
""" """
Requires no arguments. Returns your internal user object, Requires no arguments. Returns your internal user object,
@ -192,7 +179,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def user_get(self, args, database, user, **kwargs): def user_get(self, args, database, user, **kwargs):
""" """
Retreive an external user object for the given `user`. Retreive an external user object for the given `user`.
@ -204,7 +190,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def thread_index(self, args, database, user, **kwargs): def thread_index(self, args, database, user, **kwargs):
""" """
Return an array with all the threads, ordered by most recent activity. Return an array with all the threads, ordered by most recent activity.
@ -216,7 +201,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def thread_create(self, args, database, user, **kwargs): def thread_create(self, args, database, user, **kwargs):
""" """
Creates a new thread and returns it. Requires the non-empty Creates a new thread and returns it. Requires the non-empty
@ -225,12 +209,11 @@ class API(object):
validate(args, ["body", "title"]) validate(args, ["body", "title"])
thread = db.thread_create( thread = db.thread_create(
database, user["user_id"], args["body"], args["title"]) database, user["user_id"], args["body"], args["title"])
cherrypy.thread_data.usermap = {user["user_id"]: user} cherrypy.thread_data.usermap = thread
return thread return thread
@api_method @api_method
@cherrypy.expose
def thread_reply(self, args, database, user, **kwargs): def thread_reply(self, args, database, user, **kwargs):
""" """
Creates a new reply for the given thread and returns it. Creates a new reply for the given thread and returns it.
@ -242,7 +225,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def thread_load(self, args, database, user, **kwargs): def thread_load(self, args, database, user, **kwargs):
""" """
Returns the thread object with all of its messages loaded. Returns the thread object with all of its messages loaded.
@ -256,7 +238,6 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def edit_post(self, args, database, user, **kwargs): def edit_post(self, args, database, user, **kwargs):
""" """
Replace a post with a new body. Requires the arguments Replace a post with a new body. Requires the arguments
@ -265,8 +246,9 @@ class API(object):
otherwise an error object is returned whose description otherwise an error object is returned whose description
should be shown to the user. should be shown to the user.
To perform sanity checks without actually attempting to To perform sanity checks and retrieve the unformatted body
edit a post, use `edit_query` of a post without actually attempting to replace it, use
`edit_query` first.
Returns the new message object. Returns the new message object.
""" """
@ -278,14 +260,14 @@ class API(object):
@api_method @api_method
@cherrypy.expose
def edit_query(self, args, database, user, **kwargs): def edit_query(self, args, database, user, **kwargs):
""" """
Queries the database to ensure the user can edit a given Queries the database to ensure the user can edit a given
message. Requires the arguments `thread_id` and `post_id` message. Requires the arguments `thread_id` and `post_id`
(does not require a new body) (does not require a new body)
Returns either boolean true or the current message object Returns the original message object without any formatting
on success.
""" """
if user == db.anon: if user == db.anon:
raise BBJUserError("Anons cannot edit messages.") raise BBJUserError("Anons cannot edit messages.")
@ -294,10 +276,10 @@ class API(object):
database, user["user_id"], args["thread_id"], args["post_id"]) database, user["user_id"], args["thread_id"], args["post_id"])
@cherrypy.expose
def test(self, **kwargs): def test(self, **kwargs):
print(cherrypy.request.body.read()) print(cherrypy.request.body.read())
return "{\"wow\": \"jolly good show!\"}" return "{\"wow\": \"jolly good show!\"}"
test.exposed = True

View File

@ -36,13 +36,6 @@ def thread_get(connection, thread_id, messages=True):
MESSAGES, if False, will omit the inclusion of a thread's messages MESSAGES, if False, will omit the inclusion of a thread's messages
and only get its metadata, such as title, author, etc. and only get its metadata, such as title, author, etc.
FORMATTER should be a callable object who takes a body string
as it's only argument and returns an object to be sent in the
response. It isn't strictly necessary that it returns a string,
for example the entity parser will return an array with the
body string and another array with indices and types of objects
contained in it.
""" """
c = connection.cursor() c = connection.cursor()
c.execute("SELECT * FROM threads WHERE thread_id = ?", (thread_id,)) c.execute("SELECT * FROM threads WHERE thread_id = ?", (thread_id,))
@ -53,12 +46,11 @@ def thread_get(connection, thread_id, messages=True):
thread = schema.thread(*thread) thread = schema.thread(*thread)
if messages: if messages:
c.execute("SELECT * FROM messages WHERE thread_id = ?", (thread_id,)) c.execute("""SELECT * FROM messages WHERE thread_id = ?
ORDER BY post_id""", (thread_id,))
# create a dictionary where each message is accessible by its # create a dictionary where each message is accessible by its
# integer post_id as a key # integer post_id as a key
thread["messages"] = \ thread["messages"] = [schema.message(*values) for values in c.fetchall()]
{message["post_id"]: message for message in
[schema.message(*values) for values in c.fetchall()]}
return thread return thread