Source code for caspia.homeserver.homeserver

import asyncio
import logging

import sanic
from cached_property import cached_property
from sanic_cors import CORS
from tinydb import TinyDB

from caspia.meadow.client import ConsumerConnection, ServiceBrowser

from . import authorization, exceptions, services, views

logger = logging.getLogger(__name__)

ROUTE_TABLE = {
    '/services': views.ServicesView,
    '/services/<service_name>': views.ServiceView,
    '/services/<service_name>/characteristics/<characteristic_name>/value':
    views.CharacteristicValueView,
    '/notifications': views.NotificationsView,
    '/walls': views.WallsView,
    '/walls/<identifier:int>': views.WallView,
    '/walls/<wall_identifier:int>/tiles': views.TilesView,
    '/walls/<wall_identifier:int>/tiles/<tile_identifier>': views.TileView,
}


[docs]class Homeserver: def __init__(self, *, host, port, broker_url, name, cors_origins=[r'.*'], storage, db: TinyDB, config: dict, loop): self.host = host self.port = port self.name = name self.broker_url = broker_url self.storage = storage self.db = db self.config = config self.loop = loop self.cors_origins = cors_origins
[docs] def create_service(self, service_cls, **kwargs): base_kwargs = dict(loop=self.loop, db=self.db, config=self.config) return service_cls(**base_kwargs, **kwargs)
@cached_property def consumer_conn(self): conn = ConsumerConnection(self.broker_url, name=self.name) asyncio.ensure_future(conn.run_forever(), loop=self.loop) return conn @cached_property def service_browser(self): return ServiceBrowser(connection=self.consumer_conn, loop=self.loop) @cached_property def meadow_service(self) -> services.MeadowService: return self.create_service(services.MeadowService, browser=self.service_browser, consumer_conn=self.consumer_conn) @cached_property def wall_service(self) -> services.WallService: return self.create_service(services.WallService) @cached_property def tile_service(self) -> services.TileService: return self.create_service(services.TileService, wall_service=self.wall_service) @cached_property def user_service(self) -> services.UserService: return self.create_service(services.UserService) @cached_property def metadata_service(self) -> services.MetadataService: return self.create_service(services.MetadataService, storage=self.storage, broker_url=self.broker_url, consumer_conn=self.consumer_conn) @cached_property def services(self): return { services.MeadowService: self.meadow_service, services.WallService: self.wall_service, services.TileService: self.tile_service, services.UserService: self.user_service, services.MetadataService: self.metadata_service, }
[docs] def setup_routes(self, app: sanic.app.Sanic): for uri, view in ROUTE_TABLE.items(): app.add_route(view.as_view(self.services, self.loop), uri=uri, version=1)
@cached_property def app(self): app = sanic.app.Sanic('homeserver') app.enable_websocket(True) self.setup_routes(app) app.listener('before_server_start')(self.before_server_start) authorization.setup(self, app) CORS(app, supports_credentials=True, always_send=True, origins=self.cors_origins or '*') app.exception(sanic.exceptions.SanicException)(self.handle_server_exception) app.exception(exceptions.HomeserverException)(self.handle_server_exception) self.metadata_service # ensure the service exists pylint: disable=pointless-statement return app
[docs] async def handle_server_exception(self, request, exception): status = getattr(exception, 'status_code', 500) message = 'internal server error' if isinstance(exception, sanic.exceptions.SanicException): status = exception.status_code message = str(exception) if isinstance(exception, exceptions.HomeserverException): status = exception.status_code message = exception.message return sanic.response.json({'message': message}, status=status)
[docs] async def before_server_start(self, app, loop): await self.service_browser.prepare()