from aiohttp.web import Application, Response, get, post, HTTPBadRequest, HTTPInternalServerError, HTTPNotFound from json import dumps from traceback import print_exc from tiramisu import Config from .dispatcher import dispatcher from .utils import _ from .context import Context from .error import CallError, NotAllowedError, RegistrationError from .message import get_messages from .logger import log from .config import DEBUG, HTTP_PORT from .services import load_services def create_context(request): risotto_context = Context() risotto_context.username = request.match_info.get('username', "Anonymous") return risotto_context def register(version: str, path: str): """ Decorator to register function to the http route """ def decorator(function): if path in extra_routes: raise RegistrationError(f'the route {path} is already registered') extra_routes[path] = {'function': function, 'version': version} return decorator class extra_route_handler: async def __new__(cls, request): kwargs = dict(request.match_info) kwargs['request'] = request kwargs['risotto_context'] = create_context(request) kwargs['risotto_context'].version = cls.version kwargs['risotto_context'].paths.append(cls.path) kwargs['risotto_context'].type = 'http_get' function_name = cls.function.__module__ # if not 'api' function if function_name != 'risotto.http': module_name = function_name.split('.')[-2] kwargs['self'] = dispatcher.injected_self[module_name] try: returns = await cls.function(**kwargs) except NotAllowedError as err: raise HTTPNotFound(reason=str(err)) except CallError as err: raise HTTPBadRequest(reason=str(err)) except Exception as err: if DEBUG: print_exc() raise HTTPInternalServerError(reason=str(err)) log.info_msg(kwargs['risotto_context'], dict(request.match_info)) return Response(text=dumps(returns)) async def handle(request): version, uri = request.match_info.get_info()['path'].rsplit('/', 2)[-2:] risotto_context = create_context(request) kwargs = await request.json() try: text = await dispatcher.call(version, uri, risotto_context, public_only=True, **kwargs) except NotAllowedError as err: raise HTTPNotFound(reason=str(err)) except CallError as err: raise HTTPBadRequest(reason=str(err)) except Exception as err: if DEBUG: print_exc() raise HTTPInternalServerError(reason=str(err)) return Response(text=dumps({'response': text})) async def api(request, risotto_context): global tiramisu if not tiramisu: config = Config(get_messages(load_shortarg=True, only_public=True)[1]) config.property.read_write() tiramisu = config.option.dict(remotable='none') return tiramisu extra_routes = {'': {'function': api, 'version': 'v1'}} async def get_app(loop): """ build all routes """ global extra_routes load_services() app = Application(loop=loop) routes = [] for version, messages in dispatcher.messages.items(): print() print(_('======== Registered messages ========')) for message in messages: web_message = f'/api/{version}/{message}' if dispatcher.messages[version][message]['public']: print(f' - {web_message}') else: pattern = dispatcher.messages[version][message]['pattern'] print(f' - {web_message} (private {pattern})') routes.append(post(web_message, handle)) print() print(_('======== Registered extra routes ========')) for path, extra in extra_routes.items(): version = extra['version'] path = f'/api/{version}{path}' extra['path'] = path extra_handler = type(path, (extra_route_handler,), extra) routes.append(get(path, extra_handler)) print(f' - {path} (http_get)') # routes.append(get(f'/api/{version}', api)) print() del extra_routes app.add_routes(routes) await dispatcher.on_join() return await loop.create_server(app.make_handler(), '*', HTTP_PORT) tiramisu = None