import json, bottle, logging from bottle import request as bottle_req, response as bottle_resp, static_file from .endpoint import EndpointHandler from .login import LoginHandler, LogoutHandler, increase_cookie_timestamp from .actions import Action from . import exceptions, http from . import application as app_control from .single_request import SingleRequestHandler from functools import wraps application = bottle.Bottle() log = logging.getLogger('ray') def to_json(fnc): @wraps(fnc) def inner(*args, **kwargs): bottle_resp.headers['Content-Type'] = 'application/json' from_func = fnc(*args, **kwargs) if from_func is not None: return json.dumps({'result': from_func}) return inner @application.route('/static/<filepath:path>') def server_static(filepath): return static_file(filepath, root='static') @application.route('/api/<url:re:.+>', method=['GET', 'POST', 'PUT', 'DELETE'], apply=to_json) def dispatch(url): """ This class is the beginning of all entrypoints in the Ray API. Here, each url will be redirect to the right handler """ url = bottle_req.path log.info('request: %s', bottle_req.url) if url[-1] == '/': url = url[:-1] response_code = 200 try: processed = process(url, bottle_req, bottle_resp) try: from_func, http_status = processed[0], processed[1] bottle_resp.status = http_status return from_func except: return processed except exceptions.RayException as e: log.exception('ray exception: ') response_code = e.http_code except: log.exception('exception:') raise bottle_resp.status = response_code def process(fullpath, request, response): if __is_login(fullpath): return LoginHandler.process(request, response) elif __is_ping_status(fullpath): return _handle_ping_status() elif __is_logout(fullpath): return LogoutHandler.logout(response) elif _is_single_url(fullpath): return SingleRequestHandler.process(fullpath, bottle_req.method.lower(), bottle_req, bottle_resp) elif _is_endpoint(fullpath): return EndpointHandler(request, fullpath).process() elif _is_action(fullpath): return __handle_action(fullpath), 200 raise exceptions.BadRequest() def _handle_ping_status(): cookie_name, user_token = increase_cookie_timestamp() bottle_resp.set_cookie(cookie_name, user_token.decode('utf-8')) def __handle_action(url): # url e.g: /api/user/123/action arg = None if len(url.split('/')) >= 5: # indicate that has an id between endpoint and action_name arg = http.param_at(url, -2) return Action(url, arg, bottle_req).process_action() def _is_single_url(fullpath): return app_control.has_single_url(fullpath) def __is_ping_status(fullpath): return fullpath == '/api/_ping' def __is_login(full_path): return full_path == '/api/_login' def __is_logout(full_path): return full_path == '/api/_logout' def _is_endpoint(full_path): full_path = full_path.split('?')[0] if len(full_path.split('/')) == 4: try: int(full_path.split('/')[-1]) return True except: return False return len(full_path.split('/')) <= 4 and len(full_path.split('/')) > 2 def _is_action(full_path): # full_path e.g: /api/user/123/action # full_path e.g: /api/user/action if len(full_path.split('/')) >= 4: try: int(full_path.split('/')[-1]) except Exception: return True return len(full_path.split('/')) == 5