221 lines
5.7 KiB
Python
221 lines
5.7 KiB
Python
|
from src.exceptions import BBJException, BBJParameterError
|
||
|
from src import db, schema, endpoints
|
||
|
from functools import wraps
|
||
|
import cherrypy
|
||
|
import sqlite3
|
||
|
import json
|
||
|
|
||
|
dbname = "data.sqlite"
|
||
|
|
||
|
with sqlite3.connect(dbname) as _c:
|
||
|
if not db.user_resolve(_c, "anonymous"):
|
||
|
db.user_register(_c, *db.anonymous)
|
||
|
|
||
|
|
||
|
# creates a database connection for each thread
|
||
|
def connect(_):
|
||
|
cherrypy.thread_data.db = sqlite3.connect(dbname)
|
||
|
cherrypy.engine.subscribe('start_thread', connect)
|
||
|
|
||
|
|
||
|
def bbjapi(function):
|
||
|
"""
|
||
|
A wrapper that handles encoding of objects and errors to a
|
||
|
standard format for the API, resolves and authorizes users
|
||
|
from header data, and prepares thread data to handle the
|
||
|
request.
|
||
|
|
||
|
In addition, all BBJException's will return their attached
|
||
|
schema, and unhandled exceptions return a code 1 error schema.
|
||
|
"""
|
||
|
@wraps(function)
|
||
|
def wrapper(*args, **kwargs):
|
||
|
headers = cherrypy.request.headers
|
||
|
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:
|
||
|
value = function(*args, **kwargs)
|
||
|
|
||
|
except BBJException as e:
|
||
|
value = e.schema
|
||
|
|
||
|
except Exception as e:
|
||
|
value = schema.error(1, str(e))
|
||
|
|
||
|
return json.dumps(value)
|
||
|
return wrapper
|
||
|
|
||
|
|
||
|
def create_usermap(connection, obj):
|
||
|
"""
|
||
|
Creates a mapping of all the user_ids that occur in OBJ to
|
||
|
their full user objects (names, profile info, etc). Can
|
||
|
be a thread_index or a messages object from one.
|
||
|
"""
|
||
|
|
||
|
if isinstance(obj, dict):
|
||
|
# this is a message object for a thread, unravel it
|
||
|
obj = [value for key, value in obj.items()]
|
||
|
|
||
|
return {
|
||
|
user_id: db.user_resolve(
|
||
|
connection,
|
||
|
user_id,
|
||
|
externalize=True,
|
||
|
return_false=False)
|
||
|
for user_id in {
|
||
|
item["author"] for item in obj
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
def validate(json, args):
|
||
|
"""
|
||
|
Ensure the json object contains all the keys needed to satisfy
|
||
|
its endpoint.
|
||
|
"""
|
||
|
for arg in args:
|
||
|
if arg not in json.keys():
|
||
|
raise BBJParameterError(
|
||
|
"Required parameter %s is "
|
||
|
"absent from the request." % arg)
|
||
|
|
||
|
|
||
|
APICONFIG = {
|
||
|
"/": {
|
||
|
"tools.response_headers.on": True,
|
||
|
"tools.response_headers.headers": [
|
||
|
("Content-Type", "application/json")
|
||
|
],
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class API(object):
|
||
|
@bbjapi
|
||
|
@cherrypy.expose
|
||
|
def thread_index(self):
|
||
|
threads = db.thread_index(cherrypy.thread_data.db)
|
||
|
usermap = create_usermap(cherrypy.thread_data.db, threads)
|
||
|
return schema.response({
|
||
|
"data": threads,
|
||
|
"usermap": usermap
|
||
|
})
|
||
|
|
||
|
|
||
|
@bbjapi
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_in()
|
||
|
def thread_create(self):
|
||
|
args = cherrypy.request.json
|
||
|
validate(args, ["body", "title"])
|
||
|
|
||
|
thread = db.thread_create(
|
||
|
cherrypy.thread_data.db,
|
||
|
cherrypy.thread_data.user["user_id"],
|
||
|
args["body"], args["title"])
|
||
|
|
||
|
usermap = {
|
||
|
cherrypy.thread_data.user["user_id"]:
|
||
|
cherrypy.thread_data.user
|
||
|
}
|
||
|
|
||
|
return schema.response({
|
||
|
"data": thread,
|
||
|
"usermap": usermap
|
||
|
})
|
||
|
|
||
|
|
||
|
@bbjapi
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_in()
|
||
|
def thread_reply(self):
|
||
|
args = cherrypy.request.json
|
||
|
validate(args, ["thread_id", "body"])
|
||
|
return schema.response({
|
||
|
"data": db.thread_reply(
|
||
|
cherrypy.thread_data.db,
|
||
|
cherrypy.thread_data.user["user_id"],
|
||
|
args["thread_id"], args["body"])
|
||
|
})
|
||
|
|
||
|
|
||
|
@bbjapi
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_in()
|
||
|
def thread_load(self):
|
||
|
args = cherrypy.request.json
|
||
|
validate(args, ["thread_id"])
|
||
|
|
||
|
thread = db.thread_get(
|
||
|
cherrypy.thread_data.db,
|
||
|
args["thread_id"])
|
||
|
|
||
|
usermap = create_usermap(
|
||
|
cherrypy.thread_data.db,
|
||
|
thread["messages"])
|
||
|
|
||
|
return schema.response({
|
||
|
"data": thread,
|
||
|
"usermap": usermap
|
||
|
})
|
||
|
|
||
|
|
||
|
@bbjapi
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_in()
|
||
|
def user_register(self):
|
||
|
args = cherrypy.request.json
|
||
|
validate(args, ["user_name", "auth_hash"])
|
||
|
return schema.response({
|
||
|
"data": db.user_register(
|
||
|
cherrypy.thread_data.db,
|
||
|
args["user_name"], args["auth_hash"])
|
||
|
})
|
||
|
|
||
|
|
||
|
@bbjapi
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_in()
|
||
|
def edit_query(self):
|
||
|
args = cherrypy.request.json
|
||
|
validate(args, ["thread_id", "post_id"])
|
||
|
return schema.response({
|
||
|
"data": message_edit_query(
|
||
|
cherrypy.thread_data.db,
|
||
|
cherrypy.thread_data.user["user_id"],
|
||
|
args["thread_id"],
|
||
|
args["post_id"])
|
||
|
})
|
||
|
|
||
|
|
||
|
|
||
|
def run():
|
||
|
cherrypy.quickstart(API(), "/api")
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
print("wew")
|