add getting company

This commit is contained in:
2024-04-10 00:33:31 +03:00
parent 361378929f
commit 0e2ecd3449
21 changed files with 142 additions and 83 deletions

View File

@@ -7,6 +7,8 @@ from api.application.protocols.jwt import JwtTokenProcessor
from api.application.protocols.password_hasher import PasswordHasher
from api.application.usecase.auth.auth_user import LoginUser
from api.application.usecase.auth.create_user import CreateUser
from api.application.usecase.company.get_users_company import GetCompaniesByOwnerEmail
from api.domain.company.repository import CompanyRepository
from api.domain.user.repository import UserRepository
from api.infrastructure.auth.jwt_settings import JwtSettings
from api.infrastructure.dependencies.adapters import (
@@ -26,8 +28,14 @@ from api.infrastructure.dependencies.protocols import (
get_password_hasher,
get_user_login,
)
from api.infrastructure.dependencies.repositories import get_user_repository
from api.infrastructure.dependencies.usecases import provide_create_user
from api.infrastructure.dependencies.repositories import (
get_company_repository,
get_user_repository,
)
from api.infrastructure.dependencies.usecases import (
provide_create_user,
provide_get_companies_by_email,
)
from api.infrastructure.persistence.db_setings import DBSettings
from api.infrastructure.settings import Settings
@@ -50,5 +58,7 @@ def init_dependencies(app: FastAPI) -> None:
app.dependency_overrides[LoginUser] = get_user_login
app.dependency_overrides[UserRepository] = get_user_repository
app.dependency_overrides[CompanyRepository] = get_company_repository
app.dependency_overrides[CreateUser] = provide_create_user
app.dependency_overrides[GetCompaniesByOwnerEmail] = provide_get_companies_by_email

View File

@@ -4,8 +4,8 @@ from contextlib import asynccontextmanager
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncEngine
from api.app_builder.dependencies import init_dependencies
from api.app_builder.error_handlers import init_exc_handlers
from api.app_entrypoint.dependencies import init_dependencies
from api.app_entrypoint.error_handlers import init_exc_handlers
from api.infrastructure.auth.jwt_settings import JwtSettings
from api.infrastructure.dependencies.adapters import create_engine
from api.infrastructure.dependencies.configs import (

View File

@@ -1,13 +1,13 @@
from typing import Protocol
from api.domain.user.model import UserId
from api.domain.user.model import UserEmail, UserId
class JwtTokenProcessor(Protocol):
def generate_token(self, user_id: UserId) -> str:
def generate_token(self, user_id: UserId, user_email: UserEmail) -> str:
raise NotImplementedError
def validate_token(self, token: str) -> UserId | None:
def validate_token(self, token: str) -> tuple[UserId, UserEmail] | None:
raise NotImplementedError
def refresh_token(self, token: str) -> str:

View File

@@ -4,7 +4,6 @@ from uuid import UUID, uuid4
from api.domain import DomainValidationError
from api.domain.entity import DomainEntity
from api.domain.user.model import User
from api.domain.value_obj import DomainValueObject
@@ -41,13 +40,11 @@ class CompanyId(DomainValueObject):
class Company(DomainEntity[CompanyId]):
name: CompanyName
email: CompanyEmail
owner: User
@staticmethod
def create(name: str, email: str, owner: User) -> "Company":
def create(name: str, email: str) -> "Company":
return Company(
id=CompanyId(uuid4()),
name=CompanyName(name),
email=CompanyEmail(email),
owner=owner,
)

View File

@@ -1,15 +1,14 @@
from typing import Protocol
from api.domain.company.model import Company
from api.domain.user.model import User
class CompanyRepository(Protocol):
async def get_companies_by_owner_email(self, filter: dict) -> list[Company]:
raise NotImplementedError
async def create_company(self, company: Company) -> None:
raise NotImplementedError
async def get_workes_list(self) -> list[User]:
raise NotImplementedError
# async def create_company(self, company: Company) -> None:
# raise NotImplementedError
#
# async def get_workes_list(self) -> list[User]:
# raise NotImplementedError

View File

@@ -7,7 +7,7 @@ from jose.jwt import decode, encode
from api.application.protocols.date_time import DateTimeProvider
from api.application.protocols.jwt import JwtTokenProcessor
from api.domain.user.error import UserInvalidCredentialsError
from api.domain.user.model import UserId
from api.domain.user.model import UserEmail, UserId
from api.infrastructure.auth.jwt_settings import JwtSettings
@@ -16,7 +16,7 @@ class JoseJwtTokenProcessor(JwtTokenProcessor):
self.jwt_options = jwt_options
self.date_time_provider = date_time_provider
def generate_token(self, user_id: UserId) -> str:
def generate_token(self, user_id: UserId, user_email: UserEmail) -> str:
issued_at = self.date_time_provider.get_current_time()
expiration_time = issued_at + timedelta(minutes=self.jwt_options.expires_in)
@@ -24,19 +24,20 @@ class JoseJwtTokenProcessor(JwtTokenProcessor):
"iat": issued_at,
"exp": expiration_time,
"sub": str(user_id.value),
"email": user_email.value,
}
return encode(claims, self.jwt_options.secret, self.jwt_options.algorithm)
def validate_token(self, token: str) -> UserId | None:
def validate_token(self, token: str) -> tuple[UserId, UserEmail] | None:
try:
payload = decode(token, self.jwt_options.secret, [self.jwt_options.algorithm])
return UserId(UUID(payload["sub"]))
return UserId(UUID(payload["sub"])), UserEmail(payload["email"])
except (JWTError, ValueError, KeyError):
return None
def refresh_token(self, token: str) -> str:
user = self.validate_token(token)
if user is None:
token_data = self.validate_token(token)
if token_data is None:
raise UserInvalidCredentialsError("invalid token")
return self.generate_token(user)
return self.generate_token(token_data[0], token_data[1])

View File

@@ -3,7 +3,11 @@ from typing import Annotated
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from api.domain.company.repository import CompanyRepository
from api.domain.user import UserRepository
from api.infrastructure.persistence.repositories.company_repository import (
SqlAlchemyCompanyRepository,
)
from api.infrastructure.persistence.repositories.user_repository import (
SqlAlchemyUserRepository,
)
@@ -15,3 +19,9 @@ def get_user_repository(
session: Annotated[AsyncSession, Depends(Stub(AsyncSession))],
) -> UserRepository:
return SqlAlchemyUserRepository(session)
def get_company_repository(
session: Annotated[AsyncSession, Depends(Stub(AsyncSession))],
) -> CompanyRepository:
return SqlAlchemyCompanyRepository(session)

View File

@@ -5,6 +5,8 @@ from fastapi import Depends
from api.application.abstractions.uow import UnitOfWork
from api.application.protocols.password_hasher import PasswordHasher
from api.application.usecase.auth.create_user import CreateUser
from api.application.usecase.company.get_users_company import GetCompaniesByOwnerEmail
from api.domain.company.repository import CompanyRepository
from api.domain.user.repository import UserRepository
from api.infrastructure.dependencies.stub import Stub
@@ -15,3 +17,9 @@ def provide_create_user(
password_hasher: Annotated[PasswordHasher, Depends(Stub(PasswordHasher))],
) -> CreateUser:
return CreateUser(uow=uow, user_repository=user_repository, password_hasher=password_hasher)
def provide_get_companies_by_email(
company_repository: Annotated[CompanyRepository, Depends(Stub(CompanyRepository))],
) -> GetCompaniesByOwnerEmail:
return GetCompaniesByOwnerEmail(company_repository=company_repository)

View File

@@ -1,7 +1,9 @@
from .base import Base
from .company import CompanyModel
from .user import UserModel
__all__ = (
"Base",
"UserModel",
"CompanyModel",
)

View File

@@ -0,0 +1,17 @@
import uuid
from sqlalchemy import UUID
from sqlalchemy.orm import Mapped, mapped_column
from api.infrastructure.persistence.models.base import Base
class CompanyModel(Base):
__tablename__ = "companies"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
primary_key=True,
)
name: Mapped[str]
email: Mapped[str] = mapped_column(unique=True)

View File

@@ -1,45 +1,45 @@
# from sqlalchemy import text
# from sqlalchemy.ext.asyncio import AsyncSession
#
# from api.domain.company import CompanyRepository, company
# from api.domain.user.model import UserEmail, UserFirstName, UserId
#
#
# class SqlAlchemyUserRepository(UserRepository):
# def __init__(self, session: AsyncSession) -> None:
# self.session = session
#
# async def create_user(self, user: User) -> None:
# 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,
# },
# )
#
# async def get_user(self, filter: dict) -> User | None:
# stmt = text("""SELECT * FROM users WHERE email = :val""")
# result = await self.session.execute(stmt, {"val": filter["email"]})
#
# result = result.mappings().one_or_none()
#
# if result is None:
# return None
#
# return User(
# id=UserId(result.id),
# name=UserFirstName(result.name),
# email=UserEmail(result.email),
# hashed_password=result.hashed_password,
# )
#
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
from api.domain.company.model import Company, CompanyEmail, CompanyId, CompanyName
from api.domain.company.repository import CompanyRepository
class SqlAlchemyCompanyRepository(CompanyRepository):
def __init__(self, session: AsyncSession) -> None:
self.session = session
# async def create_user(self, user: User) -> None:
# 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,
# },
# )
#
async def get_companies_by_owner_email(self, filter: dict) -> list[Company]:
stmt = text("""SELECT * FROM companies WHERE email = :val""")
result = await self.session.execute(stmt, {"val": filter["email"]})
result = result.mappings().all()
return [
Company(
id=CompanyId(c.id),
name=CompanyName(c.name),
email=CompanyEmail(c.email),
)
for c in result
]
# async def get_users(self) -> list[User]:
# return []

View File

@@ -8,7 +8,7 @@ from api.application.contracts.auth.auth_response import AuthenticationResponse
from api.application.protocols.jwt import JwtTokenProcessor
from api.application.usecase.auth.auth_user import LoginUser
from api.application.usecase.auth.create_user import CreateUser
from api.domain.user.model import UserId
from api.domain.user.model import UserEmail, UserId
from api.infrastructure.dependencies.stub import Stub
auth_router = APIRouter(prefix="/auth", tags=["Auth"])
@@ -35,7 +35,7 @@ async def login(
password=login_request.password,
)
)
token = token_processor.generate_token(UserId(user.id))
token = token_processor.generate_token(UserId(user.id), UserEmail(user.email))
response.set_cookie(key="access_token", value=f"Bearer {token}", httponly=True)
return user

View File

@@ -1,6 +1,13 @@
from typing import Annotated
from fastapi import APIRouter, Depends, Request
from api.application.contracts.company.company_request import CompanyByOwnerEmail
from api.application.contracts.company.company_response import CompanyBaseResponse
from api.application.protocols.jwt import JwtTokenProcessor
from api.application.usecase.company.get_users_company import GetCompaniesByOwnerEmail
from api.domain.user.error import UserValidationError
from api.infrastructure.dependencies.stub import Stub
from api.presentation.auth.fasapi_auth import auth_required
company_router = APIRouter(prefix="/company", tags=["Company"])
@@ -11,8 +18,19 @@ company_router = APIRouter(prefix="/company", tags=["Company"])
response_model=None,
dependencies=[Depends(auth_required)],
)
async def get_company(request: Request) -> CompanyBaseResponse:
return CompanyBaseResponse(
name="some",
email="some",
)
async def get_companies(
request: Request,
token_processor: Annotated[JwtTokenProcessor, Depends(Stub(JwtTokenProcessor))],
usecase: Annotated[GetCompaniesByOwnerEmail, Depends(Stub(GetCompaniesByOwnerEmail))],
) -> list[CompanyBaseResponse]:
token_data = token_processor.validate_token(request.scope["auth"])
if not token_data:
raise UserValidationError("Login required")
companies = await usecase.execute(request=CompanyByOwnerEmail(email=token_data[1].value))
return [
CompanyBaseResponse(
name=c.name,
email=c.email,
)
for c in companies
]