naming/styling changes; change base response schema

pull/4/head
Blake DeMarcy 2017-04-02 03:34:52 -05:00
parent 26b6dc1907
commit 3640d1b1de
4 changed files with 102 additions and 83 deletions

169
server.py
View File

@ -5,66 +5,70 @@ import cherrypy
import sqlite3 import sqlite3
import json import json
dbname = "data.sqlite"
dbname = "data.sqlite"
with sqlite3.connect(dbname) as _c: with sqlite3.connect(dbname) as _c:
if not db.user_resolve(_c, "anonymous"): db.anon_object = db.user_resolve(_c, "anonymous")
db.user_register(_c, *db.anonymous) if not db.anon_object:
db.anon_object = db.user_register(_c, *db.anon_credentials)
# creates a database connection for each thread # creates a database connection for each thread
def connect(_): def db_connect(_):
cherrypy.thread_data.db = sqlite3.connect(dbname) cherrypy.thread_data.db = sqlite3.connect(dbname)
cherrypy.engine.subscribe('start_thread', connect) cherrypy.engine.subscribe('start_thread', db_connect)
def bbjapi(function): 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 thread data to handle the from header data, and prepares cherrypy.thread_data so other
request. funtions can handle the request.
In addition, all BBJException's will return their attached In the body of each api method and all the functions
schema, and unhandled exceptions return a code 1 error schema. they utilize, BBJExceptions are caught and their attached
schema is dispatched to the client. All other unhandled
exceptions will throw a code 1 back at the client and log
it for inspection.
""" """
@wraps(function) @wraps(function)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
headers = cherrypy.request.headers response = None
username = headers.get("User")
auth = headers.get("Auth")
anon = False
if not username and not auth:
user = db.user_resolve(cherrypy.thread_data.db, "anonymous")
anon = True
elif not username or not auth:
return json.dumps(schema.error(5,
"User or Auth was given without the other."))
if not anon:
user = db.user_resolve(cherrypy.thread_data.db, username)
if not user:
return json.dumps(schema.error(4,
"Username is not registered."))
elif auth != user["auth_hash"]:
return json.dumps(schema.error(5,
"Invalid authorization key for user."))
cherrypy.thread_data.user = user
cherrypy.thread_data.anon = anon
try: try:
value = function(*args, **kwargs) username = cherrypy.request.headers.get("User")
auth = cherrypy.request.headers.get("Auth")
anon = False
if not username and not auth:
user = db.anon_object
anon = True
elif not username or not auth:
return json.dumps(schema.error(5,
"User or Auth was given without the other."))
if not anon:
user = db.user_resolve(cherrypy.thread_data.db, username)
if auth != user["auth_hash"]:
return json.dumps(schema.error(5,
"Invalid authorization key for user."))
cherrypy.thread_data.user = user
cherrypy.thread_data.anon = anon
response = function(*args, **kwargs)
except BBJException as e: except BBJException as e:
value = e.schema response = e.schema
except Exception as e: except Exception as e:
value = schema.error(1, str(e)) response = schema.error(1, repr(e))
# TODO: use a logging file or module or something
# repr() in this case is more verbose than just passing it in
print(repr(e))
finally:
return json.dumps(response)
return json.dumps(value)
return wrapper return wrapper
@ -76,8 +80,8 @@ def create_usermap(connection, obj):
""" """
if isinstance(obj, dict): if isinstance(obj, dict):
# this is a message object for a thread, unravel it # this is a message object for a thread, ditch the keys
obj = [value for key, value in obj.items()] obj = obj.values()
return { return {
user_id: db.user_resolve( user_id: db.user_resolve(
@ -114,18 +118,40 @@ APICONFIG = {
} }
class API(object): class API(object):
@bbjapi @api_method
@cherrypy.expose
def get_me(self):
"""
Requires no arguments. Returns your internal user object,
including your authorization hash.
"""
return schema.response(cherrypy.thread_data.user)
@api_method
@cherrypy.expose
def user_get(self):
"""
Retreive an external user object for the given `user`.
Can be a user_id or user_name.
"""
args = cherrypy.request.json
validate(["user"])
return schema.response(db.user_resolve(
cherrypy.thread_data.db,
args["user"],
return_false=False,
externalize=True))
@api_method
@cherrypy.expose @cherrypy.expose
def thread_index(self): def thread_index(self):
threads = db.thread_index(cherrypy.thread_data.db) threads = db.thread_index(cherrypy.thread_data.db)
usermap = create_usermap(cherrypy.thread_data.db, threads) usermap = create_usermap(cherrypy.thread_data.db, threads)
return schema.response({ return schema.response(threads, usermap)
"data": threads,
"usermap": usermap
})
@bbjapi @api_method
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
def thread_create(self): def thread_create(self):
@ -142,27 +168,22 @@ class API(object):
cherrypy.thread_data.user cherrypy.thread_data.user
} }
return schema.response({ return schema.response(thread, usermap)
"data": thread,
"usermap": usermap
})
@bbjapi @api_method
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
def thread_reply(self): def thread_reply(self):
args = cherrypy.request.json args = cherrypy.request.json
validate(args, ["thread_id", "body"]) validate(args, ["thread_id", "body"])
return schema.response({ return schema.response(db.thread_reply(
"data": db.thread_reply( cherrypy.thread_data.db,
cherrypy.thread_data.db, cherrypy.thread_data.user["user_id"],
cherrypy.thread_data.user["user_id"], args["thread_id"], args["body"]))
args["thread_id"], args["body"])
})
@bbjapi @api_method
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
def thread_load(self): def thread_load(self):
@ -177,38 +198,32 @@ class API(object):
cherrypy.thread_data.db, cherrypy.thread_data.db,
thread["messages"]) thread["messages"])
return schema.response({ return schema.response(thread, usermap)
"data": thread,
"usermap": usermap
})
@bbjapi @api_method
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
def user_register(self): def user_register(self):
args = cherrypy.request.json args = cherrypy.request.json
validate(args, ["user_name", "auth_hash"]) validate(args, ["user_name", "auth_hash"])
return schema.response({ return schema.response(db.user_register(
"data": db.user_register( cherrypy.thread_data.db,
cherrypy.thread_data.db, args["user_name"],
args["user_name"], args["auth_hash"]) args["auth_hash"]))
})
@bbjapi @api_method
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
def edit_query(self): def edit_query(self):
args = cherrypy.request.json args = cherrypy.request.json
validate(args, ["thread_id", "post_id"]) validate(args, ["thread_id", "post_id"])
return schema.response({ return schema.response(message_edit_query(
"data": message_edit_query( cherrypy.thread_data.db,
cherrypy.thread_data.db, cherrypy.thread_data.user["user_id"],
cherrypy.thread_data.user["user_id"], args["thread_id"],
args["thread_id"], args["post_id"]))
args["post_id"])
})

View File

@ -2,7 +2,7 @@
set -e set -e
if [[ $1 == --init ]]; then if [[ $1 == --init ]]; then
sqlite3 bbj.db < schema.sql sqlite3 data.sqlite < schema.sql
echo cleared echo cleared
exit exit
fi fi

View File

@ -25,7 +25,8 @@ import pickle
import json import json
import os import os
anonymous = \ anon_object = None
anon_credentials = \
("anonymous", ("anonymous",
"5430eeed859cad61d925097ec4f53246" "5430eeed859cad61d925097ec4f53246"
"1ccf1ab6b9802b09a313be1478a4d614") "1ccf1ab6b9802b09a313be1478a4d614")
@ -228,7 +229,7 @@ def user_resolve(connection, name_or_id, externalize=False, return_false=True):
return False return False
raise BBJParameterError( raise BBJParameterError(
"Requested user element ({})" "Requested user element ({})"
" does not exist".format(name_or_id)) " is not registered".format(name_or_id))
def user_update(connection, user_object, parameters): def user_update(connection, user_object, parameters):

View File

@ -1,12 +1,15 @@
def base(): def base():
return { return {
"error": False "error": False,
"data": None,
"usermap": dict()
} }
def response(dictionary): def response(data, usermap={}):
result = base() result = base()
result.update(dictionary) result["data"] = data
result["usermap"].update(usermap)
return result return result