Skip to content

Commit

Permalink
big refactor of application and modules
Browse files Browse the repository at this point in the history
  • Loading branch information
pgorecki committed Jul 5, 2023
1 parent 2f24d47 commit 5bade23
Show file tree
Hide file tree
Showing 67 changed files with 1,294 additions and 1,768 deletions.
61 changes: 18 additions & 43 deletions src/api/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,34 @@
from collections.abc import Callable
from typing import Annotated

from fastapi import Depends, HTTPException, Request
from fastapi.security import OAuth2PasswordBearer

from config.container import Container, inject
from modules.iam.domain.entities import User
from seedwork.application import Application

from .shared import dependency
from modules.iam.application.services import IamService
from modules.iam.domain.entities import AnonymousUser
from seedwork.application import Application, TransactionContext

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def allow_role(role: str):
def check(user: User):
if user.role != role:
raise HTTPException(status_code=403, detail="Forbidden")
return True

return check


def allow_authenticated():
def check(user: User):
if not user.is_authenticated():
raise HTTPException(status_code=403, detail="Forbidden")
return True
async def get_application(request: Request) -> Application:
application = request.app.container.application()
return application

return check

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

def allow_anonymous():
return lambda user: True


def create_app(check: callable) -> Callable[..., Application]:
@inject
async def create(
request: Request, app: Application = dependency(Container.application)
):
with app.transaction_context() as ctx:
try:
access_token = await oauth2_scheme(request=request)
current_user = app.iam_service.find_user_by_access_token(access_token)
current_user = ctx.get_service(IamService).find_user_by_access_token(
access_token
)
except HTTPException as e:
current_user = User.Anonymous()

print("current user", current_user)
check(current_user)
app.current_user = current_user
return app

return create
current_user = AnonymousUser()

ctx.dependency_provider["current_user"] = current_user

def get_current_active_user(
app: Application = Depends(create_app(allow_authenticated())),
):
return app.current_user
yield ctx
26 changes: 8 additions & 18 deletions src/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

import api.routers.catalog
from api.models.catalog import CurrentUser
from api.routers import bidding, catalog, diagnostics, iam
from config.api_config import ApiConfig
from config.container import Container
from config.container import TopLevelContainer
from seedwork.domain.exceptions import DomainException, EntityNotFoundException
from seedwork.infrastructure.logging import LoggerFactory, logger
from seedwork.infrastructure.request_context import request_context
Expand All @@ -16,17 +14,8 @@
LoggerFactory.configure(logger_name="api")

# dependency injection container
container = Container()
container = TopLevelContainer()
container.config.from_pydantic(ApiConfig())
container.wire(
modules=[
api.dependencies,
api.routers.catalog,
api.routers.bidding,
api.routers.iam,
api.routers.diagnostics,
]
)

app = FastAPI(debug=container.config.DEBUG)
app.include_router(catalog.router)
Expand All @@ -35,14 +24,14 @@
app.include_router(diagnostics.router)
app.container = container

logger.info("using db engine %s" % str(container.engine()))
# logger.info("using db engine %s" % str(container.engine()))


@app.exception_handler(DomainException)
async def unicorn_exception_handler(request: Request, exc: DomainException):
return JSONResponse(
status_code=500,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
content={"message": f"Oops! {exc} did something. There goes a rainbow..."},
)


Expand All @@ -51,22 +40,23 @@ async def unicorn_exception_handler(request: Request, exc: EntityNotFoundExcepti
return JSONResponse(
status_code=404,
content={
"message": f"Entity {exc.entity_id} not found in {exc.repository.__class__.__name__}"
"message": f"Entity {exc.kwargs} not found in {exc.repository.__class__.__name__}"
},
)


@app.middleware("http")
async def add_request_context(request: Request, call_next):
start_time = time.time()
request_context.begin_request(current_user=CurrentUser.fake_user())
# request_context.begin_request(current_user=CurrentUser.fake_user())
try:
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
finally:
request_context.end_request()
pass
# request_context.end_request()


@app.get("/")
Expand Down
12 changes: 0 additions & 12 deletions src/api/models/common.py

This file was deleted.

18 changes: 9 additions & 9 deletions src/api/routers/bidding.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from fastapi import APIRouter
from typing import Annotated

from fastapi import APIRouter, Depends

from api.dependencies import get_application
from api.models.bidding import BiddingResponse, PlaceBidRequest
from api.shared import dependency
from config.container import Container, inject
from config.container import inject
from modules.bidding.application.command import PlaceBidCommand, RetractBidCommand
from modules.bidding.application.query.get_bidding_details import GetBiddingDetails
from seedwork.application import Application
Expand All @@ -17,8 +19,7 @@
@router.get("/bidding/{listing_id}", tags=["bidding"], response_model=BiddingResponse)
@inject
async def get_bidding_details_of_listing(
listing_id,
app: Application = dependency(Container.application),
listing_id, app: Annotated[Application, Depends(get_application)]
):
"""
Shows listing details
Expand All @@ -40,7 +41,7 @@ async def get_bidding_details_of_listing(
async def place_bid(
listing_id,
request_body: PlaceBidRequest,
app: Application = dependency(Container.application),
app: Annotated[Application, Depends(get_application)],
):
"""
Places a bid on a listing
Expand All @@ -52,7 +53,7 @@ async def place_bid(
bidder_id=request_body.bidder_id,
amount=request_body.amount,
)
app.execute_command(command)
result = app.execute_command(command)

query = GetBiddingDetails(listing_id=listing_id)
query_result = app.execute_query(query)
Expand All @@ -71,8 +72,7 @@ async def place_bid(
)
@inject
async def retract_bid(
listing_id,
app: Application = dependency(Container.application),
listing_id, app: Annotated[Application, Depends(get_application)]
):
"""
Retracts a bid from a listing
Expand Down
28 changes: 13 additions & 15 deletions src/api/routers/catalog.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from fastapi import APIRouter
from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends

from api.dependencies import Application, get_application
from api.models.catalog import ListingIndexModel, ListingReadModel, ListingWriteModel
from api.shared import dependency
from config.container import Container, inject
from config.container import inject
from modules.catalog.application.command import (
CreateListingDraftCommand,
DeleteListingDraftCommand,
Expand All @@ -23,9 +26,7 @@

@router.get("/catalog", tags=["catalog"], response_model=ListingIndexModel)
@inject
async def get_all_listings(
app: Application = dependency(Container.application),
):
async def get_all_listings(app: Annotated[Application, Depends(get_application)]):
"""
Shows all published listings in the catalog
"""
Expand All @@ -37,8 +38,7 @@ async def get_all_listings(
@router.get("/catalog/{listing_id}", tags=["catalog"], response_model=ListingReadModel)
@inject
async def get_listing_details(
listing_id,
app: Application = dependency(Container.application),
listing_id, app: Annotated[Application, Depends(get_application)]
):
"""
Shows listing details
Expand All @@ -54,7 +54,7 @@ async def get_listing_details(
@inject
async def create_listing(
request_body: ListingWriteModel,
app: Application = dependency(Container.application),
app: Annotated[Application, Depends(get_application)],
):
"""
Creates a new listing.
Expand All @@ -77,8 +77,7 @@ async def create_listing(
)
@inject
async def delete_listing(
listing_id,
app: Application = dependency(Container.application),
listing_id, app: Annotated[Application, Depends(get_application)]
):
"""
Delete listing
Expand All @@ -97,17 +96,16 @@ async def delete_listing(
)
@inject
async def publish_listing(
listing_id,
app: Application = dependency(Container.application),
listing_id: UUID, app: Annotated[Application, Depends(get_application)]
):
"""
Creates a new listing.
"""
command = PublishListingDraftCommand(
listing_id=listing_id,
)
command_result = app.execute_command(command)
app.execute_command(command)

query = GetListingDetails(listing_id=command_result.entity_id)
query = GetListingDetails(listing_id=listing_id)
query_result = app.execute_query(query)
return dict(query_result.payload)
19 changes: 8 additions & 11 deletions src/api/routers/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,21 @@

from fastapi import APIRouter, Depends

from api.dependencies import allow_anonymous, create_app
from seedwork.application import Application

router = APIRouter()

from api.dependencies import get_transaction_context
from seedwork.application import TransactionContext

from .iam import UserResponse

app_for_anonymous = create_app(allow_anonymous())
router = APIRouter()


@router.get("/debug", tags=["diagnostics"])
async def debug(app: Annotated[Application, Depends(app_for_anonymous)]):
async def debug(ctx: Annotated[TransactionContext, Depends(get_transaction_context)]):
return dict(
app_id=id(app),
app_id=id(ctx.app),
user=UserResponse(
id=str(app.current_user.id), username=app.current_user.username
id=str(ctx.current_user.id), username=ctx.current_user.username
),
name=app.name,
version=app.version,
name=ctx.app.name,
version=ctx.app.version,
)
30 changes: 19 additions & 11 deletions src/api/routers/iam.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

from api.dependencies import get_current_active_user
from api.shared import dependency
from config.container import Container, inject
from api.dependencies import (
Application,
TransactionContext,
get_application,
get_transaction_context,
)
from config.container import inject
from modules.iam.application.exceptions import InvalidCredentialsException
from modules.iam.domain.entities import User
from seedwork.application import Application
from modules.iam.application.services import IamService

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
router = APIRouter()
Expand All @@ -19,18 +24,19 @@ class UserResponse(BaseModel):


@router.get("/token", tags=["iam"])
async def get_token(token: str = Depends(oauth2_scheme)):
return "sample_token"
async def get_token(app: Annotated[Application, Depends(get_application)]):
return app.current_user.access_token


@router.post("/token", tags=["iam"])
@inject
async def login(
ctx: Annotated[TransactionContext, Depends(get_transaction_context)],
form_data: OAuth2PasswordRequestForm = Depends(),
app: Application = dependency(Container.application),
):
try:
user = app.iam_service.authenticate_with_password(
iam_service = ctx.get_service(IamService)
user = iam_service.authenticate_with_name_and_password(
form_data.username, form_data.password
)
except InvalidCredentialsException:
Expand All @@ -44,5 +50,7 @@ async def login(


@router.get("/users/me", tags=["iam"])
async def get_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
async def get_users_me(
app: Annotated[Application, Depends(get_application)],
):
return app.current_user
7 changes: 0 additions & 7 deletions src/api/shared.py

This file was deleted.

Loading

0 comments on commit 5bade23

Please sign in to comment.