auth and raw sql in alchemy

main
Сергей Ванюшкин 2024-04-01 09:19:10 +00:00
parent 4e6aee8c3a
commit 949ea9fdcf
19 changed files with 71 additions and 40 deletions

View File

@ -1,16 +1,15 @@
from fastapi import FastAPI from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker from sqlalchemy.ext.asyncio import (AsyncEngine, AsyncSession,
async_sessionmaker)
from api.application.abstractions.uow import UnitOfWork from api.application.abstractions.uow import UnitOfWork
from api.application.protocols.password_hasher import PasswordHasher from api.application.protocols.password_hasher import PasswordHasher
from api.application.usecase.user.create_user import CreateUser from api.application.usecase.auth.create_user import CreateUser
from api.domain.user.repository import UserRepository from api.domain.user.repository import UserRepository
from api.infrastructure.dependencies.adapters import ( from api.infrastructure.dependencies.adapters import (create_engine,
create_engine,
create_session_maker, create_session_maker,
new_session, new_session,
new_unit_of_work, new_unit_of_work)
)
from api.infrastructure.dependencies.configs import app_settings from api.infrastructure.dependencies.configs import app_settings
from api.infrastructure.dependencies.protocols import get_password_hasher from api.infrastructure.dependencies.protocols import get_password_hasher
from api.infrastructure.dependencies.repositories import get_user_repository from api.infrastructure.dependencies.repositories import get_user_repository

View File

@ -21,6 +21,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
engine = app.dependency_overrides[AsyncEngine](app.dependency_overrides[Settings]()) engine = app.dependency_overrides[AsyncEngine](app.dependency_overrides[Settings]())
async with engine.begin() as conn: async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all) await conn.run_sync(Base.metadata.create_all)
yield yield

View File

@ -1,8 +1,10 @@
from fastapi import FastAPI from fastapi import FastAPI
from api.presentation.routers import healthcheck_router, user_router from api.presentation.routers import (auth_router, healthcheck_router,
user_router)
def init_routers(app: FastAPI) -> None: def init_routers(app: FastAPI) -> None:
app.include_router(user_router) app.include_router(user_router)
app.include_router(auth_router)
app.include_router(healthcheck_router) app.include_router(healthcheck_router)

View File

View File

@ -0,0 +1,3 @@
from .auth_request import UserCreateRequest
__all__ = ("UserCreateRequest",)

View File

@ -0,0 +1,8 @@
from dataclasses import dataclass
@dataclass(frozen=True)
class UserCreateRequest:
name: str
email: str
password: str

View File

@ -1,9 +1,8 @@
from .user_request import GetUserByEmailRequest, UserCreateRequest from .user_request import GetUserByEmailRequest
from .user_response import UserDetaledResponse, UserResponse from .user_response import UserDetaledResponse, UserResponse
__all__ = ( __all__ = (
"UserResponse", "UserResponse",
"UserDetaledResponse", "UserDetaledResponse",
"UserCreateRequest",
"GetUserByEmailRequest", "GetUserByEmailRequest",
) )

View File

@ -1,13 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
@dataclass(frozen=True)
class UserCreateRequest:
name: str
email: str
password: str
@dataclass(frozen=True) @dataclass(frozen=True)
class GetUserByEmailRequest: class GetUserByEmailRequest:
email: str email: str

View File

View File

@ -1,5 +1,5 @@
from api.application.abstractions import UnitOfWork from api.application.abstractions import UnitOfWork
from api.application.contracts.user.user_request import UserCreateRequest from api.application.contracts.auth.auth_request import UserCreateRequest
from api.application.protocols.password_hasher import PasswordHasher from api.application.protocols.password_hasher import PasswordHasher
from api.domain.user.model import User from api.domain.user.model import User
from api.domain.user.repository import UserRepository from api.domain.user.repository import UserRepository

View File

View File

@ -4,7 +4,7 @@ from fastapi import Depends
from api.application.abstractions.uow import UnitOfWork from api.application.abstractions.uow import UnitOfWork
from api.application.protocols.password_hasher import PasswordHasher from api.application.protocols.password_hasher import PasswordHasher
from api.application.usecase.user.create_user import CreateUser from api.application.usecase.auth.create_user import CreateUser
from api.domain.user.repository import UserRepository from api.domain.user.repository import UserRepository
from api.infrastructure.dependencies.stub import Stub from api.infrastructure.dependencies.stub import Stub
@ -14,4 +14,6 @@ def provide_create_user(
uow: Annotated[UnitOfWork, Depends(Stub(UnitOfWork))], uow: Annotated[UnitOfWork, Depends(Stub(UnitOfWork))],
password_hasher: Annotated[PasswordHasher, Depends(Stub(PasswordHasher))], password_hasher: Annotated[PasswordHasher, Depends(Stub(PasswordHasher))],
) -> CreateUser: ) -> CreateUser:
return CreateUser(uow=uow, user_repository=user_repository, password_hasher=password_hasher) return CreateUser(
uow=uow, user_repository=user_repository, password_hasher=password_hasher
)

View File

@ -7,7 +7,7 @@ from api.infrastructure.persistence.models.base import Base
class UserModel(Base): class UserModel(Base):
__tablename__ = "user" __tablename__ = "users"
id: Mapped[uuid.UUID] = mapped_column( id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), UUID(as_uuid=True),

View File

@ -1,4 +1,4 @@
from sqlalchemy import insert from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from api.domain.user import User, UserRepository from api.domain.user import User, UserRepository
@ -10,13 +10,26 @@ class SqlAlchemyUserRepository(UserRepository):
self.session = session self.session = session
async def create_user(self, user: User) -> None: async def create_user(self, user: User) -> None:
stmt = insert(UserModel).values( # stmt = insert(UserModel).values(
id=user.id.value, # id=user.id.value,
name=user.name.value, # name=user.name.value,
email=user.email.value, # email=user.email.value,
hashed_password=user.hashed_password, # hashed_password=user.hashed_password,
# )
stmt = text(
"""INSERT INTO users (id, name, email, hashed_password)
VALUES(:id, :name, :email, :hashed_password)
"""
)
await self.session.execute(
stmt,
{
"id": str(user.id.value),
"name": user.name.value,
"email": user.email.value,
"hashed_password": user.hashed_password,
},
) )
await self.session.execute(stmt)
async def get_user(self, filter: dict) -> User | None: async def get_user(self, filter: dict) -> User | None:
pass pass

View File

@ -1,7 +1,9 @@
from .auth import auth_router
from .ping import healthcheck_router from .ping import healthcheck_router
from .user import user_router from .user import user_router
__all__ = ( __all__ = (
"healthcheck_router", "healthcheck_router",
"auth_router",
"user_router", "user_router",
) )

View File

@ -0,0 +1,17 @@
from typing import Annotated
from fastapi import APIRouter, Depends
from api.application.contracts.auth import UserCreateRequest
from api.application.usecase.auth.create_user import CreateUser
from api.infrastructure.dependencies.stub import Stub
auth_router = APIRouter(prefix="/auth", tags=["Auth"])
@auth_router.post("/register", status_code=201)
async def create_user(
request: UserCreateRequest,
usecase: Annotated[CreateUser, Depends(Stub(CreateUser))],
) -> None:
return await usecase.execute(request)

View File

@ -2,8 +2,7 @@ from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from api.application.contracts.user import UserCreateRequest, UserResponse from api.application.contracts.user import UserResponse
from api.application.usecase.user.create_user import CreateUser
from api.infrastructure.dependencies.stub import Stub from api.infrastructure.dependencies.stub import Stub
user_router = APIRouter(prefix="/users", tags=["Users"]) user_router = APIRouter(prefix="/users", tags=["Users"])
@ -12,11 +11,3 @@ user_router = APIRouter(prefix="/users", tags=["Users"])
@user_router.get("/") @user_router.get("/")
async def get_all_users() -> list[UserResponse]: async def get_all_users() -> list[UserResponse]:
return [] return []
@user_router.post("/")
async def create_user(
request: UserCreateRequest,
usecase: Annotated[CreateUser, Depends(Stub(CreateUser))],
) -> None:
return await usecase.execute(request)

View File

@ -1,5 +1,5 @@
db: db:
host: "db" host: "localhost"
port: 5432 port: 5432
database: "serviceman_db" database: "serviceman_db"
user: "demo_user" user: "demo_user"

1
poetry.lock generated
View File

@ -1132,6 +1132,7 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},