diff --git a/docs/src/index.rst b/docs/src/index.rst index 0609dfd0d..577cb7620 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -59,8 +59,8 @@ Give me an ASGI web app! piccolo asgi new -FastAPI, Starlette, BlackSheep, and Litestar are currently supported, with more -coming soon. +FastAPI, Starlette, BlackSheep, Litestar and Esmerald are currently supported, +with more coming soon. ------------------------------------------------------------------------------- diff --git a/docs/src/piccolo/asgi/index.rst b/docs/src/piccolo/asgi/index.rst index 67bf87027..57f1553c4 100644 --- a/docs/src/piccolo/asgi/index.rst +++ b/docs/src/piccolo/asgi/index.rst @@ -21,8 +21,8 @@ Routing frameworks ****************** Currently, `Starlette `_, `FastAPI `_, -`BlackSheep `_, and -`Litestar `_ are supported. +`BlackSheep `_, +`Litestar `_ and `Esmerald `_ are supported. Other great ASGI routing frameworks exist, and may be supported in the future (`Quart `_ , @@ -32,8 +32,8 @@ Other great ASGI routing frameworks exist, and may be supported in the future Which to use? ============= -All are great choices. FastAPI is built on top of Starlette, so they're -very similar. FastAPI and BlackSheep are great if you want to document a REST +All are great choices. FastAPI and Esmerald are built on top of Starlette, so they're +very similar. FastAPI, BlackSheep and Esmerald are great if you want to document a REST API, as they have built-in OpenAPI support. ------------------------------------------------------------------------------- diff --git a/piccolo/apps/asgi/commands/new.py b/piccolo/apps/asgi/commands/new.py index 0618a6814..38a056295 100644 --- a/piccolo/apps/asgi/commands/new.py +++ b/piccolo/apps/asgi/commands/new.py @@ -10,7 +10,7 @@ TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates/app/") SERVERS = ["uvicorn", "Hypercorn"] -ROUTERS = ["starlette", "fastapi", "blacksheep", "litestar"] +ROUTERS = ["starlette", "fastapi", "blacksheep", "litestar", "esmerald"] ROUTER_DEPENDENCIES = { "fastapi": ["fastapi>=0.100.0"], } diff --git a/piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja b/piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja new file mode 100644 index 000000000..a69df7706 --- /dev/null +++ b/piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja @@ -0,0 +1,100 @@ +import typing as t + +from pathlib import Path + +from piccolo.utils.pydantic import create_pydantic_model +from piccolo.engine import engine_finder + +from esmerald import ( + Esmerald, + Include, + Gateway, + JSONResponse, + APIView, + get, + post, + put, + delete +) +from esmerald.config import StaticFilesConfig + +from home.endpoints import home +from home.piccolo_app import APP_CONFIG +from home.tables import Task + + +async def open_database_connection_pool(): + try: + engine = engine_finder() + await engine.start_connection_pool() + except Exception: + print("Unable to connect to the database") + + +async def close_database_connection_pool(): + try: + engine = engine_finder() + await engine.close_connection_pool() + except Exception: + print("Unable to connect to the database") + + +TaskModelIn: t.Any = create_pydantic_model( + table=Task, + model_name='TaskModelIn' +) +TaskModelOut: t.Any = create_pydantic_model( + table=Task, + include_default_columns=True, + model_name='TaskModelOut' +) + + +class TaskAPIView(APIView): + path: str = "/" + tags: str = ["Task"] + + @get("/") + async def tasks(self) -> t.List[TaskModelOut]: + return await Task.select().order_by(Task.id) + + + @post('/') + async def create_task(self, payload: TaskModelIn) -> TaskModelOut: + task = Task(**payload.dict()) + await task.save() + return task.to_dict() + + + @put('/{task_id}') + async def update_task(self, payload: TaskModelIn, task_id: int) -> TaskModelOut: + task = await Task.objects().get(Task.id == task_id) + if not task: + return JSONResponse({}, status_code=404) + + for key, value in payload.dict().items(): + setattr(task, key, value) + + await task.save() + + return task.to_dict() + + + @delete('/{task_id}') + async def delete_task(self, task_id: int) -> None: + task = await Task.objects().get(Task.id == task_id) + if not task: + return JSONResponse({}, status_code=404) + + await task.remove() + + +app = Esmerald( + routes=[ + Gateway("/", handler=home), + Gateway("/tasks", handler=TaskAPIView) + ], + static_files_config=StaticFilesConfig(path="/static", directory=Path("static")), + on_startup=[open_database_connection_pool], + on_shutdown=[close_database_connection_pool], +) diff --git a/piccolo/apps/asgi/commands/templates/app/app.py.jinja b/piccolo/apps/asgi/commands/templates/app/app.py.jinja index 2a8de80da..234286a3b 100644 --- a/piccolo/apps/asgi/commands/templates/app/app.py.jinja +++ b/piccolo/apps/asgi/commands/templates/app/app.py.jinja @@ -6,4 +6,6 @@ {% include '_blacksheep_app.py.jinja' %} {% elif router == 'litestar' %} {% include '_litestar_app.py.jinja' %} +{% elif router == 'esmerald' %} + {% include '_esmerald_app.py.jinja' %} {% endif %} diff --git a/piccolo/apps/asgi/commands/templates/app/home/_esmerald_endpoints.py.jinja b/piccolo/apps/asgi/commands/templates/app/home/_esmerald_endpoints.py.jinja new file mode 100644 index 000000000..a8c9bfdff --- /dev/null +++ b/piccolo/apps/asgi/commands/templates/app/home/_esmerald_endpoints.py.jinja @@ -0,0 +1,20 @@ +import os + +import jinja2 +from esmerald import Request, Response, get +from esmerald.responses import HTMLResponse + +ENVIRONMENT = jinja2.Environment( + loader=jinja2.FileSystemLoader( + searchpath=os.path.join(os.path.dirname(__file__), "templates") + ) +) + + +@get(path="/", include_in_schema=False) +def home(request: Request) -> HTMLResponse: + template = ENVIRONMENT.get_template("home.html.jinja") + + content = template.render(title="Piccolo + ASGI",) + + return HTMLResponse(content) diff --git a/piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja b/piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja index fc5011b24..cbce94e2f 100644 --- a/piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja +++ b/piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja @@ -4,4 +4,6 @@ {% include '_blacksheep_endpoints.py.jinja' %} {% elif router == 'litestar' %} {% include '_litestar_endpoints.py.jinja' %} +{% elif router == 'esmerald' %} + {% include '_esmerald_endpoints.py.jinja' %} {% endif %} diff --git a/piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw b/piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw index ab175a6b0..644bc4265 100644 --- a/piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw +++ b/piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw @@ -56,6 +56,11 @@
  • Admin
  • Swagger API
  • +

    Esmerald

    + {% endblock content %}