def __init__( self, components: typing.Optional[typing.List[Component]] = None, debug: bool = False, title: typing.Optional[str] = "", version: typing.Optional[str] = "", description: typing.Optional[str] = "", schema: typing.Optional[str] = "/schema/", docs: typing.Optional[str] = "/docs/", redoc: typing.Optional[str] = None, on_startup: typing.Sequence[typing.Callable] = None, on_shutdown: typing.Sequence[typing.Callable] = None, *args, **kwargs ) -> None: super().__init__(debug=debug, *args, **kwargs) if components is None: components = [] # Initialize injector self.components = components self.router = Router(components=components, on_startup=on_startup, on_shutdown=on_shutdown) self.app = self.router self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.error_middleware = ServerErrorMiddleware(self.exception_middleware, debug=debug) # Add exception handler for API exceptions self.add_exception_handler(exceptions.HTTPException, self.api_http_exception_handler) # Add schema and docs routes self.add_schema_docs_routes( title=title, version=version, description=description, schema=schema, docs=docs, redoc=redoc )
def test_mount_router(self, router, component_mock): router.components = [component_mock] app = Router() router.mount("/app/", app=app) assert len(router.routes) == 1 assert isinstance(router.routes[0], Mount) assert router.routes[0].path == "/app" assert router.routes[0].app == app assert router.routes[0].app.components == [component_mock]
class Flama(Starlette, SchemaMixin): def __init__( self, components: typing.Optional[typing.List[Component]] = None, debug: bool = False, title: typing.Optional[str] = "", version: typing.Optional[str] = "", description: typing.Optional[str] = "", schema: typing.Optional[str] = "/schema/", docs: typing.Optional[str] = "/docs/", redoc: typing.Optional[str] = None, *args, **kwargs ) -> None: super().__init__(debug=debug, *args, **kwargs) if components is None: components = [] # Initialize injector self.components = components self.router = Router(components=components) self.app = self.router self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.error_middleware = ServerErrorMiddleware(self.exception_middleware, debug=debug) # Add exception handler for API exceptions self.add_exception_handler(exceptions.HTTPException, self.api_http_exception_handler) # Add schema and docs routes self.add_schema_docs_routes( title=title, version=version, description=description, schema=schema, docs=docs, redoc=redoc ) @property def injector(self): return Injector(components=self.components) def mount(self, path: str, app: ASGIApp, name: str = None) -> None: self.components += getattr(app, "components", []) self.router.mount(path, app=app, name=name) def add_resource(self, path: str, resource: "BaseResource"): self.router.add_resource(path, resource=resource) def resource(self, path: str) -> typing.Callable: def decorator(resource: "BaseResource") -> "BaseResource": self.router.add_resource(path, resource=resource) return resource return decorator def api_http_exception_handler(self, request: Request, exc: HTTPException) -> Response: return APIErrorResponse(detail=exc.detail, status_code=exc.status_code, exception=exc)
def test_get_route_from_scope_nested_mount_view(self, app, router, scope): @router.route("/foo/") async def foo(): return "foo" app.mount("/router", app=Router(routes=[Mount("/nested", app=router)])) scope["path"] = "/router/nested/foo/" scope["method"] = "GET" route, route_scope = app.router.get_route_from_scope(scope=scope) assert route_scope is not None assert route.endpoint == foo assert route.path == "/foo/" assert route_scope["path"] == "/foo/" assert route_scope["root_path"] == "/router/nested" assert route_scope["endpoint"] == foo
def app(self): app_ = Flama( components=[], title="Foo", version="0.1", description="Bar", schema="/schema/", docs="/docs/", redoc="/redoc/", ) @app_.route("/endpoint/", methods=["GET"]) class PuppyEndpoint(HTTPEndpoint): async def get(self) -> Puppy: """ description: Custom component. responses: 200: description: Component. """ return {"name": "Canna"} @app_.route("/custom-component/", methods=["GET"]) async def get(self) -> Puppy: """ description: Custom component. responses: 200: description: Component. """ return {"name": "Canna"} @app_.route("/many-custom-component/", methods=["GET"]) async def many_custom_component() -> Puppy(many=True): """ description: Many custom component. responses: 200: description: Components. """ return [{"name": "Canna"}, {"name": "Sandy"}] @app_.route("/query-param/", methods=["GET"]) async def query_param(param: str = "Foo"): """ description: Query param. responses: 200: description: Param. """ return {"name": param} @app_.route("/path-param/{param:int}/", methods=["GET"]) async def path_param(param: int): """ description: Path param. responses: 200: description: Param. """ return {"name": param} @app_.route("/body-param/", methods=["POST"]) async def body_param(param: BodyParam): """ description: Body param. responses: 200: description: Param. """ return {"name": param["name"]} @app_.route("/default-response/", methods=["GET"]) async def default_response(): """ description: Default response. """ return {"name": "Canna"} router = Router() router.add_route("/custom-component/", endpoint=get, methods=["GET"]) app_.mount("/mount", router) return app_
class Flama(Starlette, SchemaMixin): def __init__( self, components: typing.Optional[typing.List[Component]] = None, debug: bool = False, title: typing.Optional[str] = "", version: typing.Optional[str] = "", description: typing.Optional[str] = "", schema: typing.Optional[str] = "/schema/", docs: typing.Optional[str] = "/docs/", redoc: typing.Optional[str] = None, on_startup: typing.Sequence[typing.Callable] = None, on_shutdown: typing.Sequence[typing.Callable] = None, *args, **kwargs ) -> None: super().__init__(debug=debug, *args, **kwargs) if components is None: components = [] # Initialize injector self.components = components self.router = Router(components=components, on_startup=on_startup, on_shutdown=on_shutdown) self.app = self.router self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.error_middleware = ServerErrorMiddleware(self.exception_middleware, debug=debug) # Add exception handler for API exceptions self.add_exception_handler(exceptions.HTTPException, self.api_http_exception_handler) # Add schema and docs routes self.add_schema_docs_routes( title=title, version=version, description=description, schema=schema, docs=docs, redoc=redoc ) @property def injector(self): return Injector(components=self.components) def mount(self, path: str, app: ASGIApp, name: str = None) -> None: self.components += getattr(app, "components", []) self.router.mount(path, app=app, name=name) def add_resource(self, path: str, resource: "BaseResource"): self.router.add_resource(path, resource=resource) def resource(self, path: str) -> typing.Callable: def decorator(resource: "BaseResource") -> "BaseResource": self.router.add_resource(path, resource=resource) return resource return decorator def api_http_exception_handler(self, request: Request, exc: HTTPException) -> Response: return APIErrorResponse(detail=exc.detail, status_code=exc.status_code, exception=exc) def register_router(self, router: APIRouter): assert isinstance(router, APIRouter), "Registered router must be an instance of APIRouter" self.mount(router.prefix, app=router, name=router.name) def schemas( self, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ): def decorator(func): func._request_schemas = request_schemas func._response_schema = response_schema return func return decorator def route( self, path: str, methods: typing.List[str] = None, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: def decorator(func: typing.Callable) -> typing.Callable: self.router.add_route( path, func, methods=methods, name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) return func return decorator def get( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["GET"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def put( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["PUT"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def post( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["POST"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def delete( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["DELETE"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def options( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["OPTIONS"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def head( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["HEAD"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def patch( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["PATCH"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, ) def trace( self, path: str, name: str = None, include_in_schema: bool = True, status_code: int = 200, response_schema: marshmallow.Schema = None, **request_schemas: typing.Dict[str, marshmallow.Schema] ) -> typing.Callable: return self.route( path=path, methods=["TRACE"], name=name, include_in_schema=include_in_schema, status_code=status_code, response_schema=response_schema, request_schemas=request_schemas, )
def router(self): return Router()