From bf7807899c361929402d806cbf3e1cec2850aea9 Mon Sep 17 00:00:00 2001 From: Artem Litvinov Date: Sat, 30 Sep 2023 22:28:55 +0100 Subject: [PATCH] build: wip: joke example --- .../lib/api/v1/handlers/__init__.py | 3 ++- src/fastapi_app/lib/api/v1/handlers/joke.py | 22 ++++++++++++++++++ .../lib/api/v1/schemas/__init__.py | 5 ++-- src/fastapi_app/lib/api/v1/schemas/joke.py | 7 ++++++ src/fastapi_app/lib/app/app.py | 4 +++- src/fastapi_app/lib/joke/__init__.py | 3 +++ src/fastapi_app/lib/joke/services.py | 23 +++++++++++++++++++ src/fastapi_app/lib/models/joke.py | 10 ++++++++ 8 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 src/fastapi_app/lib/api/v1/handlers/joke.py create mode 100644 src/fastapi_app/lib/api/v1/schemas/joke.py create mode 100644 src/fastapi_app/lib/joke/__init__.py create mode 100644 src/fastapi_app/lib/joke/services.py create mode 100644 src/fastapi_app/lib/models/joke.py diff --git a/src/fastapi_app/lib/api/v1/handlers/__init__.py b/src/fastapi_app/lib/api/v1/handlers/__init__.py index e679678..cfea8ca 100644 --- a/src/fastapi_app/lib/api/v1/handlers/__init__.py +++ b/src/fastapi_app/lib/api/v1/handlers/__init__.py @@ -1,5 +1,6 @@ from .health import * +from .joke import get_joke, joke_router __all__ = [ - "basic_router", + "basic_router", "joke_router", "get_joke" ] diff --git a/src/fastapi_app/lib/api/v1/handlers/joke.py b/src/fastapi_app/lib/api/v1/handlers/joke.py new file mode 100644 index 0000000..50226a9 --- /dev/null +++ b/src/fastapi_app/lib/api/v1/handlers/joke.py @@ -0,0 +1,22 @@ +import fastapi + +import lib.api.v1.schemas as api_shemas +import lib.joke.services as services + +joke_router = fastapi.APIRouter() + + +@joke_router.get( + "/", + response_model=api_shemas.JokeResponse, + summary="Random joke", + description="Return a random joke from a free API.", +) +async def get_joke(joke_service: services.JokeService): + joke = await joke_service.get_joke() + if joke: + return api_shemas.JokeResponse( + joke=f"{joke.setup}\n{joke.punchline}", id=joke.id_field, category=joke.type_field + ) + + return api_shemas.JokeResponse(joke="No joke for you!", id=0, category="No category") diff --git a/src/fastapi_app/lib/api/v1/schemas/__init__.py b/src/fastapi_app/lib/api/v1/schemas/__init__.py index 3a43b09..d608846 100644 --- a/src/fastapi_app/lib/api/v1/schemas/__init__.py +++ b/src/fastapi_app/lib/api/v1/schemas/__init__.py @@ -1,5 +1,4 @@ from .base import HealthResponseModel +from .joke import JokeResponse -__all__ = [ - "HealthResponseModel", -] +__all__ = ["HealthResponseModel", "JokeResponse"] diff --git a/src/fastapi_app/lib/api/v1/schemas/joke.py b/src/fastapi_app/lib/api/v1/schemas/joke.py new file mode 100644 index 0000000..d146a9f --- /dev/null +++ b/src/fastapi_app/lib/api/v1/schemas/joke.py @@ -0,0 +1,7 @@ +import pydantic + + +class JokeResponse(pydantic.BaseModel): + id_field: int = pydantic.Field(alias="id") + joke: str + category: str diff --git a/src/fastapi_app/lib/app/app.py b/src/fastapi_app/lib/app/app.py index f20bb8d..c63283e 100644 --- a/src/fastapi_app/lib/app/app.py +++ b/src/fastapi_app/lib/app/app.py @@ -11,6 +11,7 @@ import lib.app.errors as app_errors import lib.app.settings as app_settings import lib.app.split_settings as app_split_settings import lib.clients as clients +import lib.joke.services as joke_services logger = logging.getLogger(__name__) @@ -69,12 +70,13 @@ class Application: # Services logger.info("Initializing services") + jk_serivces = joke_services.JokeService(http_client=http_client) # Handlers logger.info("Initializing handlers") liveness_probe_handler = api_v1_handlers.basic_router - + joke_handler = api_v1_handlers.get_joke(joke_service=jk_serivces) logger.info("Creating application") diff --git a/src/fastapi_app/lib/joke/__init__.py b/src/fastapi_app/lib/joke/__init__.py new file mode 100644 index 0000000..c55dd0a --- /dev/null +++ b/src/fastapi_app/lib/joke/__init__.py @@ -0,0 +1,3 @@ +from .services import JokeService + +__all__ = ["JokeService"] diff --git a/src/fastapi_app/lib/joke/services.py b/src/fastapi_app/lib/joke/services.py new file mode 100644 index 0000000..e6aaa66 --- /dev/null +++ b/src/fastapi_app/lib/joke/services.py @@ -0,0 +1,23 @@ +import logging + +import httpx +import pydantic + +import lib.models.joke as joke_models + + +class JokeService: + def __init__(self, http_client: httpx.AsyncClient): + self.http_client = http_client + self.logger = logging.getLogger(__name__) + + async def get_joke(self) -> joke_models.Joke | None: + try: + async with self.http_client as client: + response = await client.get("https://official-joke-api.appspot.com/random_joke") + content = response.json() + return joke_models.Joke(**content) + except pydantic.ValidationError as error: + self.logger.exception("Validation Error: %s", error) + except httpx.HTTPError as error: + self.logger.exception("HTTP Error: %s", error) diff --git a/src/fastapi_app/lib/models/joke.py b/src/fastapi_app/lib/models/joke.py new file mode 100644 index 0000000..c17abae --- /dev/null +++ b/src/fastapi_app/lib/models/joke.py @@ -0,0 +1,10 @@ +import pydantic + + +class Joke(pydantic.BaseModel): + """Joke model.""" + + id_field: int = pydantic.Field(alias="id") + type_field: str = pydantic.Field(alias="type") + setup: str + punchline: str