organizations #3

Merged
k1nq merged 3 commits from organizations into dev 2025-11-27 10:10:51 +00:00
3 changed files with 51 additions and 11 deletions
Showing only changes of commit ea8f0eda65 - Show all commits

View File

@ -1,15 +1,22 @@
"""Reusable FastAPI dependencies."""
from collections.abc import AsyncGenerator
from fastapi import Depends
import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
from app.core.database import get_session
from app.core.security import jwt_service, password_hasher
from app.models.user import User
from app.repositories.org_repo import OrganizationRepository
from app.repositories.user_repo import UserRepository
from app.services.auth_service import AuthService
from app.services.user_service import UserService
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.api_v1_prefix}/auth/token")
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
"""Provide a scoped database session."""
@ -21,6 +28,10 @@ def get_user_repository(session: AsyncSession = Depends(get_db_session)) -> User
return UserRepository(session=session)
def get_organization_repository(session: AsyncSession = Depends(get_db_session)) -> OrganizationRepository:
return OrganizationRepository(session=session)
def get_user_service(repo: UserRepository = Depends(get_user_repository)) -> UserService:
return UserService(user_repository=repo, password_hasher=password_hasher)
@ -33,3 +44,26 @@ def get_auth_service(
password_hasher=password_hasher,
jwt_service=jwt_service,
)
async def get_current_user(
token: str = Depends(oauth2_scheme),
repo: UserRepository = Depends(get_user_repository),
) -> User:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
)
try:
payload = jwt_service.decode(token)
sub = payload.get("sub")
if sub is None:
raise credentials_exception
user_id = int(sub)
except (jwt.PyJWTError, TypeError, ValueError):
raise credentials_exception from None
user = await repo.get_by_id(user_id)
if user is None:
raise credentials_exception
return user

View File

@ -24,7 +24,7 @@ async def list_deals(
stage: str | None = None,
owner_id: int | None = None,
order_by: str | None = None,
order: str | None = Query(default=None, regex="^(asc|desc)$"),
order: str | None = Query(default=None, pattern="^(asc|desc)$"),
) -> dict[str, str]:
"""Placeholder for deal filtering endpoint."""
_ = (status_filter,)

View File

@ -1,16 +1,22 @@
"""Organization-related API stubs."""
"""Organization-related API endpoints."""
from __future__ import annotations
from fastapi import APIRouter, status
from fastapi import APIRouter, Depends
from app.api.deps import get_current_user, get_organization_repository
from app.models.organization import OrganizationRead
from app.models.user import User
from app.repositories.org_repo import OrganizationRepository
router = APIRouter(prefix="/organizations", tags=["organizations"])
def _stub(endpoint: str) -> dict[str, str]:
return {"detail": f"{endpoint} is not implemented yet"}
@router.get("/me", response_model=list[OrganizationRead])
async def list_user_organizations(
current_user: User = Depends(get_current_user),
repo: OrganizationRepository = Depends(get_organization_repository),
) -> list[OrganizationRead]:
"""Return organizations the authenticated user belongs to."""
@router.get("/me", status_code=status.HTTP_501_NOT_IMPLEMENTED)
async def list_user_organizations() -> dict[str, str]:
"""Placeholder for returning organizations linked to the current user."""
return _stub("GET /organizations/me")
organizations = await repo.list_for_user(current_user.id)
return [OrganizationRead.model_validate(org) for org in organizations]