Skip to content

Commit

Permalink
migrate to async
Browse files Browse the repository at this point in the history
  • Loading branch information
pgorecki committed Jan 3, 2025
1 parent de8e330 commit 31d1db3
Show file tree
Hide file tree
Showing 20 changed files with 153 additions and 112 deletions.
8 changes: 1 addition & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
default_language_version:
python: python3.10
python: python3.11

repos:
# native hints instead of `from typing` | List -> list
- repo: https://github.com/sondrelg/pep585-upgrade
rev: 'v1.0' # Version to check
hooks:
- id: upgrade-type-hints

# Only for removing unused imports > Other staff done by Black
- repo: https://github.com/myint/autoflake
rev: "v1.4" # Version to check
Expand Down
16 changes: 8 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ requests = "^2.28.1"
bcrypt = "^4.0.1"
mypy = "^1.4.1"
fastapi = "^0.110.0"
lato = "^0.10.0"
pydantic-settings = "^2.2.1"
lato = "^0.12.0"

[tool.poetry.dev-dependencies]
poethepoet = "^0.10.0"
Expand Down
8 changes: 3 additions & 5 deletions src/api/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,23 @@

from fastapi import Depends, Request
from fastapi.security import OAuth2PasswordBearer
from lato import Application, TransactionContext

from modules.iam.application.services import IamService
from modules.iam.domain.entities import User
from lato import Application, TransactionContext

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


async def get_application(request: Request) -> Application:
application = request.app.container.application()
return application
return request.state.lato_application


async def get_transaction_context(
app: Annotated[Application, Depends(get_application)],
) -> TransactionContext:
"""Creates a new transaction context for each request"""

with app.transaction_context() as ctx:
async with app.transaction_context() as ctx:
yield ctx


Expand Down
30 changes: 25 additions & 5 deletions src/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pydantic import ValidationError

from api.dependencies import oauth2_scheme # noqa
from api.routers import bidding, catalog, diagnostics, iam
from config.api_config import ApiConfig
from config.container import create_application, ApplicationContainer
from config.container import ApplicationContainer
from seedwork.domain.exceptions import DomainException, EntityNotFoundException
from seedwork.infrastructure.database import Base
from seedwork.infrastructure.logging import LoggerFactory, logger
Expand All @@ -28,16 +29,27 @@
app.include_router(bidding.router)
app.include_router(iam.router)
app.include_router(diagnostics.router)
app.container = container
app.container = container # type: ignore


@app.exception_handler(ValidationError)
async def pydantic_validation_exception_handler(request: Request, exc: ValidationError):
return JSONResponse(
status_code=422,
content={
"detail": exc.errors(),
},
)


# startup

try:
import uuid

from modules.iam.application.services import IamService

with app.container.application().transaction_context() as ctx:
with container.application().transaction_context() as ctx:
iam_service = ctx[IamService]
iam_service.create_user(
user_id=uuid.UUID(int=1),
Expand All @@ -50,7 +62,7 @@


@app.exception_handler(DomainException)
async def unicorn_exception_handler(request: Request, exc: DomainException):
async def domain_exception_handler(request: Request, exc: DomainException):
if container.config.DEBUG:
raise exc

Expand All @@ -61,7 +73,9 @@ async def unicorn_exception_handler(request: Request, exc: DomainException):


@app.exception_handler(EntityNotFoundException)
async def unicorn_exception_handler(request: Request, exc: EntityNotFoundException):
async def entity_not_found_exception_handler(
request: Request, exc: EntityNotFoundException
):
return JSONResponse(
status_code=404,
content={
Expand All @@ -70,6 +84,12 @@ async def unicorn_exception_handler(request: Request, exc: EntityNotFoundExcepti
)


@app.middleware("http")
async def add_lato_application(request: Request, call_next):
request.state.lato_application = container.application()
return await call_next(request)


@app.middleware("http")
async def add_process_time(request: Request, call_next):
start_time = time.time()
Expand Down
9 changes: 5 additions & 4 deletions src/api/routers/bidding.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from typing import Annotated

from fastapi import APIRouter, Depends
from lato import Application

from api.dependencies import get_application
from api.models.bidding import BiddingResponse, PlaceBidRequest
from config.container import inject
from modules.bidding.application.command import PlaceBidCommand, RetractBidCommand
from modules.bidding.application.query.get_bidding_details import GetBiddingDetails
from lato import Application

router = APIRouter()

Expand All @@ -25,7 +25,7 @@ async def get_bidding_details_of_listing(
Shows listing details
"""
query = GetBiddingDetails(listing_id=listing_id)
result = app.execute(query)
result = await app.execute_async(query)
return BiddingResponse(
listing_id=result.id,
auction_end_date=result.ends_at,
Expand All @@ -52,10 +52,11 @@ async def place_bid(
bidder_id=request_body.bidder_id,
amount=request_body.amount,
)
app.execute(command)
await app.execute_async(command)
# execute_async, or execute?

query = GetBiddingDetails(listing_id=listing_id)
result = app.execute(query)
result = await app.execute_async(query)
return BiddingResponse(
listing_id=result.id,
auction_end_date=result.ends_at,
Expand Down
13 changes: 6 additions & 7 deletions src/api/routers/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@


@router.get("/catalog", tags=["catalog"], response_model=ListingIndexModel)
@inject
def get_all_listings(app: Annotated[Application, Depends(get_application)]):
async def get_all_listings(app: Annotated[Application, Depends(get_application)]):
"""
Shows all published listings in the catalog
"""
query = GetAllListings()
result = app.execute(query)
result = await app.execute_async(query)
return dict(data=result)


Expand All @@ -41,7 +40,7 @@ async def get_listing_details(
Shows listing details
"""
query = GetListingDetails(listing_id=listing_id)
query_result = app.execute_query(query)
query_result = await app.execute_async(query)
return dict(data=query_result.payload)


Expand Down Expand Up @@ -87,7 +86,7 @@ async def delete_listing(
listing_id=listing_id,
seller_id=current_user.id,
)
app.execute(command)
await app.execute_async(command)


@router.post(
Expand All @@ -109,8 +108,8 @@ async def publish_listing(
listing_id=listing_id,
seller_id=current_user.id,
)
app.execute(command)
await app.execute_async(command)

query = GetListingDetails(listing_id=listing_id)
response = app.execute(query)
response = await app.execute_async(query)
return response
19 changes: 13 additions & 6 deletions src/api/tests/test_bidding.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from seedwork.infrastructure.logging import logger


def setup_app_for_bidding_tests(app, listing_id, seller_id, bidder_id):
async def setup_app_for_bidding_tests(app, listing_id, seller_id, bidder_id):
logger.info("Adding users")
with app.transaction_context() as ctx:
iam_service = ctx["iam_service"]
Expand All @@ -19,15 +19,17 @@ def setup_app_for_bidding_tests(app, listing_id, seller_id, bidder_id):
password="password",
access_token="token1",
)
ctx["logger"].debug(f"Added seller: {seller_id}")

iam_service.create_user(
user_id=bidder_id,
email="[email protected]",
password="password",
access_token="token2",
)
ctx["logger"].debug(f"Added bidder: {bidder_id}")

logger.info("Adding listing")
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=listing_id,
title="Foo",
Expand All @@ -36,18 +38,23 @@ def setup_app_for_bidding_tests(app, listing_id, seller_id, bidder_id):
seller_id=seller_id,
)
)
app.execute(
logger.info(f"Created listing draft: {listing_id}")

await app.execute_async(
PublishListingDraftCommand(listing_id=listing_id, seller_id=seller_id)
)
logger.info(f"Published listing draft {listing_id} by seller {seller_id}")

logger.info("test setup complete")


@pytest.mark.integration
def test_place_bid(app, api_client):
@pytest.mark.asyncio
async def test_place_bid(app, api_client):
listing_id = GenericUUID(int=1)
seller_id = GenericUUID(int=2)
bidder_id = GenericUUID(int=3)
setup_app_for_bidding_tests(app, listing_id, seller_id, bidder_id)
await setup_app_for_bidding_tests(app, listing_id, seller_id, bidder_id)

url = f"/bidding/{listing_id}/place_bid"

Expand Down
29 changes: 17 additions & 12 deletions src/api/tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ def test_empty_catalog_list(api_client):


@pytest.mark.integration
def test_catalog_list_with_one_item(app, api_client):
@pytest.mark.asyncio
async def test_catalog_list_with_one_item(app, api_client):
# arrange
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=GenericUUID(int=1),
title="Foo",
Expand Down Expand Up @@ -48,9 +49,10 @@ def test_catalog_list_with_one_item(app, api_client):


@pytest.mark.integration
def test_catalog_list_with_two_items(app, api_client):
@pytest.mark.asyncio
async def test_catalog_list_with_two_items(app, api_client):
# arrange
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=GenericUUID(int=1),
title="Foo #1",
Expand All @@ -59,7 +61,7 @@ def test_catalog_list_with_two_items(app, api_client):
seller_id=GenericUUID(int=2),
)
)
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=GenericUUID(int=2),
title="Foo #2",
Expand All @@ -86,9 +88,10 @@ def test_catalog_create_draft_fails_due_to_incomplete_data(


@pytest.mark.integration
def test_catalog_delete_draft(app, authenticated_api_client):
@pytest.mark.asyncio
async def test_catalog_delete_draft(app, authenticated_api_client):
current_user = authenticated_api_client.current_user
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=GenericUUID(int=1),
title="Listing to be deleted",
Expand All @@ -111,11 +114,12 @@ def test_catalog_delete_non_existing_draft_returns_404(authenticated_api_client)


@pytest.mark.integration
def test_catalog_publish_listing_draft(app, authenticated_api_client):
@pytest.mark.asyncio
async def test_catalog_publish_listing_draft(app, authenticated_api_client):
# arrange
current_user = authenticated_api_client.current_user
listing_id = GenericUUID(int=1)
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=listing_id,
title="Listing to be published",
Expand All @@ -132,11 +136,12 @@ def test_catalog_publish_listing_draft(app, authenticated_api_client):
assert response.status_code == 200


def test_published_listing_appears_in_biddings(app, authenticated_api_client):
@pytest.mark.asyncio
async def test_published_listing_appears_in_biddings(app, authenticated_api_client):
# arrange
listing_id = GenericUUID(int=1)
current_user = authenticated_api_client.current_user
app.execute(
await app.execute_async(
CreateListingDraftCommand(
listing_id=listing_id,
title="Listing to be published",
Expand All @@ -145,7 +150,7 @@ def test_published_listing_appears_in_biddings(app, authenticated_api_client):
seller_id=current_user.id,
)
)
app.execute(
await app.execute_async(
PublishListingDraftCommand(
listing_id=listing_id,
seller_id=current_user.id,
Expand Down
Loading

0 comments on commit 31d1db3

Please sign in to comment.