Closed python-programmer closed 2 years ago
Hi @python-programmer
I didn't have time to reply earlier today because I was at work. 😊 Absolutely, it is possible to share the same Container
of services among several instances of applications.
For example:
from blacksheep import Application
class Foo:
def __init__(self) -> None:
self.foo = "foo"
app = Application()
app.services.add_scoped(Foo)
@app.router.get("/")
def a_home(foo: Foo):
return f"Hello, from Application A {foo.foo}"
child_app = Application(services=app.services) # <---- 🌴
@child_app.router.get("/")
def b_home(foo: Foo):
return f"Hello, from Application B {foo.foo}"
# Note: when mounting another BlackSheep application,
# make sure to handle the start and stop events of the mounted app
@app.on_start
async def handle_app_a_start(_):
await child_app.start()
@app.on_stop
async def handle_app_a_stop(_):
await child_app.stop()
app.mount("/second-app", child_app)
# The following lines are not necessary but are useful to debug with Visual Studio Code or PyCharm...
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=44567, log_level="debug")
Note that, if you like to separate the logic that configures services for your application from the front-end layer, you can configure the rodi.Container
in dedicated modules, then use it in all your applications.
Thank you @RobertoPrevato
I used the same aproach
def configure_services(app: Application, app_list: List[str], service_list):
for i_app in app_list:
app, prefix = data extraction codes from string ...
app.mount('per app prefix', app, services=service_list)
My goal is: I want to develop a pluggable-or-modular (in this case package) system
Product: | _ User App |
---|
_ Polls App
but this approach has a downside when using openapi: it cant generate openapi in a single route (Based on my research in the documentation).
for polls app http://your-site/polls/docs for users app http://your-site/users/docs ...
Brainstorming: finally I found that I can use the Router class
and in the main app:
from users.controllers import user_router
from polls.controllers import poll_router
router = Router()
router.routes.update(user_router.routes)
router.routes.update(poll_router.routes)
app = Application(router=router)
one important thing that I want for my goal is:
router should have a prefix_url
and also a tags
for open api generation
without those:
router = Router()
@docs(tags=['User']) <------
@router.get('/api/users/') <----- /api/users/
async def users(handler: Handler) -> List[UserModel]:
return await handler.get_users()
@docs(tags=['User']) <------
@router.post('/api/users/register') <----- /api/users/
async def register(handler: Handler, model: UserCreateModel) -> UserModel:
return await handler.register(model)
If you have any suggestion, please let me know
Thanks
@python-programmer some features are still missing for mounts to work properly with the generation of OpenAPI Documentation (OAD).
I will try to find the time this weekend to modify BlackSheep so that it's able to generate proper documentation for mounted apps. I think it would be nice to support both scenarios:
Side note: if you use controllers, they will have automatically a tag with the name of the controller's class.
Hi @python-programmer
Good news: I added support for generating OAD for mounted apps from parents apps, in 1.2.5
.
I will soon describe this in the documentation.
Example:
from dataclasses import dataclass
from openapidocs.v3 import Info
from blacksheep import Application
from blacksheep.server.openapi.v3 import OpenAPIHandler
parent = Application(show_error_details=True)
parent.mount_registry.auto_events = True
parent.mount_registry.handle_docs = True
docs = OpenAPIHandler(info=Info(title="Parent API", version="0.0.1"))
docs.bind_app(parent)
@dataclass
class CreateCatInput:
name: str
email: str
foo: int
@dataclass
class CreateDogInput:
name: str
email: str
example: int
@dataclass
class CreateParrotInput:
name: str
email: str
@parent.router.get("/")
def a_home():
"""Parent root."""
return "Hello, from the parent app - for information, navigate to /docs"
@parent.router.get("/cats")
def get_cats_conflicting():
"""Conflict!"""
return "CONFLICT"
child_1 = Application()
@child_1.router.get("/")
def get_cats():
"""Gets a list of cats."""
return "Gets a list of cats."
@child_1.router.post("/")
def create_cat(data: CreateCatInput):
"""Creates a new cat."""
return "Creates a new cat."
@child_1.router.delete("/{cat_id}")
def delete_cat(cat_id: str):
"""Deletes a cat by id."""
return "Deletes a cat by id."
child_2 = Application()
@child_2.router.get("/")
def get_dogs():
"""Gets a list of dogs."""
return "Gets a list of dogs."
@child_2.router.post("/")
def create_dog(data: CreateDogInput):
"""Creates a new dog."""
return "Creates a new dog."
@child_2.router.delete("/{dog_id}")
def delete_dog(dog_id: str):
"""Deletes a dog by id."""
return "Deletes a dog by id."
child_3 = Application()
@child_3.router.get("/")
def get_parrots():
"""Gets a list of parrots."""
return "Gets a list of parrots"
@child_3.router.post("/")
def create_parrot(data: CreateParrotInput):
"""Creates a new parrot."""
return "Creates a new parrot"
@child_3.router.delete("/{parrot_id}")
def delete_parrot(parrot_id: str):
"""Deletes a parrot by id."""
return "Deletes a parrot by id."
parent.mount("/cats", child_1)
parent.mount("/dogs", child_2)
parent.mount("/parrots", child_3)
if __name__ == "__main__":
import uvicorn
uvicorn.run(parent, host="127.0.0.1", port=44567, log_level="debug")
Suppose we have a system that has multiple sub apps
__ Polls App
If we register the database connection service in main app, we can access to it in sub apps (User App, Polls App)
If this feature is available, please tell us how to use it
Thank you