from sqlalchemy.ext.asyncio import AsyncSession from fastapi import Depends from fastapi.security import OAuth2PasswordBearer from typing import Callable from datetime import datetime from fooder.db import get_db_session from fooder.domain import User from fooder.repository import Repository from fooder.utils.datetime import utc_now from fooder.exc import Unauthorized class Context: """ Main API context, aggregating dependencies """ def __init__( self, repo: Repository, clock: Callable[[], datetime] = utc_now, ) -> None: self.repo = repo self.clock = clock self._user = None def set_user(self, user: User) -> None: self._user = user @property def user(self) -> User: if self._user is None: raise Unauthorized() return self._user class ContextDependency: """ Context dependecy """ def __init__( self, ) -> None: pass def __call__( self, session: AsyncSession = Depends(get_db_session), ): return Context(repo=Repository(session)) class AuthContextDependency: """ Context dependecy for authorized endpionts """ async def __call__( self, token: str = Depends(OAuth2PasswordBearer(tokenUrl="/token")), session: AsyncSession = Depends(get_db_session), ) -> Context: ctx = Context(repo = Repository(session)) from fooder.controller.token import TokenController token_ctrl = TokenController.from_access_token(ctx, token) user = await ctx.repo.user.get(User.id == token_ctrl.entity_id) if user is None: raise Unauthorized() ctx.set_user(user) return ctx