cosmos/service/servicehandler.py
"""
Copyright (C) 2014 Maruf Maniruzzaman
Website: http://cosmosframework.com
Author: Maruf Maniruzzaman
License :: OSI Approved :: MIT License
"""
from cosmos.service import requesthandler
import tornado.ioloop
import tornado.web
import tornado.options
import tornado.template
import tornado.websocket
from tornado import gen
from cosmos.service.utils import MongoObjectJSONEncoder
from cosmos.dataservice.objectservice import *
class ServiceHandler(requesthandler.RequestHandler):
def json_encode_result(self, result, is_list=False):
if is_list:
return {"_d": MongoObjectJSONEncoder().encode(result), "_cosmos_service_array_result_": True}
else:
return MongoObjectJSONEncoder().encode(result)
@gen.coroutine
def get(self, object_path):
params = object_path.split('/')
params = list(filter(len, params))
if len(params) < 1 or len(params) > 2:
raise tornado.web.HTTPError(404, "Not found")
object_name = params[0]
if not object_name:
raise tornado.web.HTTPError(404, "Not found")
id = None
if len(params) == 2:
id = params[1]
obj_serv = self.settings['object_service']
columns_str = self.get_argument("columns", None)
filter_str = self.get_argument("filter", None)
if filter_str:
query = json.loads(filter_str)
else:
query = None
if columns_str:
columns = columns_str.split(',')
columns = [column.strip() for column in columns]
else:
columns = []
preprocessor_list = obj_serv.get_operation_preprocessor(object_name, AccessType.READ)
for preprocessor in preprocessor_list:
yield preprocessor(obj_serv, object_name, query, AccessType.READ)
processor = None
processor_list = obj_serv.get_operation_processor(object_name, AccessType.READ)
assert isinstance(processor_list, list)
if processor_list and len(processor_list) == 1:
processor = processor_list[0]
else:
if len(processor_list) > 1:
logging.critical("More than one READ processor found for object {}.".format(object_name))
raise ValueError("More than one READ processor found for object {}.".format(object_name))
result = None
if id and len(id) > 0:
if processor:
cursor = processor(self.current_user, obj_serv, object_name, id, AccessType.READ, columns,
find_one=True)
else:
cursor = obj_serv.load(self.current_user, object_name, id, columns)
result = yield cursor
if not result:
raise tornado.web.HTTPError(404, "Not found")
data = self.json_encode_result(result)
else:
if processor:
cursor = processor(self.current_user, obj_serv, object_name, query, AccessType.READ, columns)
else:
cursor = obj_serv.find(self.current_user, object_name, query, columns)
# TODO: use to_list to create list
result_list = []
while (yield cursor.fetch_next):
qry_result = cursor.next_object()
result_list.append(qry_result)
result = result_list
data = self.json_encode_result(result_list, True)
post_processor_list = obj_serv.get_operation_postprocessor(object_name, AccessType.READ)
for post_processor in post_processor_list:
yield post_processor(obj_serv, object_name, result, AccessType.READ)
self.content_type = 'application/json'
self.write(data)
self.finish()
def clean_data(self, data):
assert isinstance(data, dict)
reserved_words = ["_id", "createtime", "modifytime", "owner", "g-recaptcha-response"]
for word in reserved_words:
if word in data:
del data[word]
@gen.coroutine
def post(self, object_path):
params = object_path.split('/')
params = list(filter(len, params))
object_name = params[0]
try:
data = json.loads(self.request.body.decode("utf-8"))
assert isinstance(data, dict)
except ValueError as ve:
raise tornado.web.HTTPError(400, ve.message)
# It is important that _id is not passed to save method, insert method is ok to use.
# If _id is passed to save method it could overwrite object owned by other user
self.clean_data(data)
obj_serv = self.settings['object_service']
preprocessor_list = obj_serv.get_operation_preprocessor(object_name, AccessType.INSERT)
for preprocessor in preprocessor_list:
yield preprocessor(obj_serv, object_name, data, AccessType.INSERT)
processor = None
processor_list = obj_serv.get_operation_processor(object_name, AccessType.INSERT)
assert isinstance(processor_list, list)
if processor_list and len(processor_list) == 1:
processor = processor_list[0]
else:
if len(processor_list) > 1:
logging.critical("More than one INSERT processor found for object {}.".format(object_name))
raise ValueError("More than one INSERT processor found for object {}.".format(object_name))
if processor:
promise = processor(self.current_user, obj_serv, object_name, data, AccessType.INSERT)
else:
promise = obj_serv.insert(self.current_user, object_name, data)
result = yield promise
data = self.json_encode_result(result)
post_processor_list = obj_serv.get_operation_postprocessor(object_name, AccessType.INSERT)
for post_processor in post_processor_list:
yield post_processor(obj_serv, object_name, result, AccessType.INSERT)
self.write(data)
self.finish()
@gen.coroutine
def put(self, object_path):
params = object_path.split('/')
params = list(filter(len, params))
object_name = params[0]
id = params[1]
try:
data = json.loads(self.request.body.decode("utf-8"))
assert isinstance(data, dict)
except ValueError as ve:
raise tornado.web.HTTPError(400, ve.message)
self.clean_data(data)
obj_serv = self.settings['object_service']
data['modifytime'] = str(datetime.datetime.now())
preprocessor_list = obj_serv.get_operation_preprocessor(object_name, AccessType.UPDATE)
for preprocessor in preprocessor_list:
yield preprocessor(obj_serv, object_name, data, AccessType.UPDATE)
processor = None
processor_list = obj_serv.get_operation_processor(object_name, AccessType.UPDATE)
assert isinstance(processor_list, list)
if processor_list and len(processor_list) == 1:
processor = processor_list[0]
else:
if len(processor_list) > 1:
logging.critical("More than one UPDATE processor found for object {}.".format(object_name))
raise ValueError("More than one UPDATE processor found for object {}.".format(object_name))
if processor:
promise = processor(self.current_user, obj_serv, object_name, data, AccessType.UPDATE, id=id)
else:
promise = obj_serv.update(self.current_user, object_name, id, data)
result = yield promise
data = MongoObjectJSONEncoder().encode(
{"error": result.get("err"), "n": result.get("n"), "ok": result.get("ok"),
"updatedExisting": result.get("updatedExisting")})
post_processor_list = obj_serv.get_operation_postprocessor(object_name, AccessType.UPDATE)
for post_processor in post_processor_list:
yield post_processor(obj_serv, object_name, result, AccessType.UPDATE)
self.write(data)
self.finish()
@gen.coroutine
def delete(self, object_path):
params = object_path.split('/')
params = list(filter(len, params))
object_name = params[0]
id = params[1]
obj_serv = self.settings['object_service']
preprocessor_list = obj_serv.get_operation_preprocessor(object_name, AccessType.DELETE)
for preprocessor in preprocessor_list:
yield preprocessor(obj_serv, object_name, None, AccessType.DELETE)
processor = None
processor_list = obj_serv.get_operation_processor(object_name, AccessType.DELETE)
assert isinstance(processor_list, list)
if processor_list and len(processor_list) == 1:
processor = processor_list[0]
else:
if len(processor_list) > 1:
logging.critical("More than one DELETE processor found for object {}.".format(object_name))
raise ValueError("More than one DELETE processor found for object {}.".format(object_name))
if processor:
promise = processor(self.current_user, obj_serv, object_name, None, AccessType.DELETE, id=id)
else:
promise = obj_serv.delete(self.current_user, object_name, id)
result = yield promise
data = MongoObjectJSONEncoder().encode(
{"error": result.get("err"), "n": result.get("n"), "ok": result.get("ok")})
post_processor_list = obj_serv.get_operation_postprocessor(object_name, AccessType.DELETE)
for post_processor in post_processor_list:
yield post_processor(obj_serv, object_name, result, AccessType.DELETE)
self.write(data)
self.finish()