Skip to content

Commit c0dc507

Browse files
committed
Use current user information when creating a new quiz
1 parent 8373e54 commit c0dc507

File tree

16 files changed

+112
-40
lines changed

16 files changed

+112
-40
lines changed

api_design.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
* email (String, Unique): User's email address.
6868
* password_hash (String): Hashed password for security.
6969
* created_at (DateTime): Date and time of registration.
70-
* last_login (DateTime): Date and time of last login.
7170

7271
## Quizzes Table
7372
* Table Name: **quizzes**

app/api/dependencies.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typing import Annotated
2+
3+
from fastapi import Depends, HTTPException, status
4+
from fastapi.security import OAuth2PasswordBearer
5+
6+
from app.core.security import decode_token
7+
from app.core.settings import Settings, get_settings
8+
from app.models.database import AsyncSessionDep
9+
from app.models.user import User
10+
11+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/tokens")
12+
13+
14+
async def get_current_user(
15+
db: AsyncSessionDep,
16+
token: Annotated[str, Depends(oauth2_scheme)],
17+
settings: Annotated[Settings, Depends(get_settings)],
18+
):
19+
token_data = decode_token(token=token, settings=settings)
20+
user = await User.get(db=db, id=int(token_data.sub))
21+
if not user:
22+
raise HTTPException(
23+
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
24+
)
25+
26+
return user

app/api/endpoints/login.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
status_code=status.HTTP_201_CREATED,
1818
summary="Get a new access token",
1919
)
20-
def get_access_token_from_username(
20+
async def get_access_token_from_username(
2121
db: AsyncSessionDep,
2222
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
2323
) -> Any:
2424
"""
2525
Get an OAuth2 access token from a user logging in with a username and password,
2626
to use in future requests as an authenticated user.
2727
"""
28-
user = models.User.get_by_username(db=db, username=form_data.username)
28+
user = await models.User.get_by_username(db=db, username=form_data.username)
2929
if not user:
3030
raise HTTPException(
3131
status_code=status.HTTP_400_BAD_REQUEST,

app/api/endpoints/quiz.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import app.models as models
77
import app.schemas as schemas
8+
from app.api.dependencies import get_current_user
89
from app.models.database import AsyncSessionDep
910

1011
router = APIRouter(prefix="/quizzes", tags=["quiz"])
@@ -27,7 +28,12 @@ async def get_quiz_from_id(quiz_id: int, db: AsyncSessionDep) -> models.Quiz:
2728
summary="Create a new quiz",
2829
response_description="The new created quiz",
2930
)
30-
async def create_quiz(db: AsyncSessionDep, quiz: schemas.QuizCreate) -> Any:
31+
async def create_quiz(
32+
db: AsyncSessionDep,
33+
quiz: schemas.QuizCreate,
34+
current_user: Annotated[models.User, Depends(get_current_user)],
35+
) -> Any:
36+
quiz.created_by = current_user.id
3137
new_quiz = await models.Quiz.create(db=db, quiz=quiz)
3238
return new_quiz
3339

app/core/custom_logging.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ def configure_logger():
2222

2323
# logger.level("INFO", color="<green>")
2424

25-
logging.basicConfig(handlers=[InterceptHandler()], level=0)
26-
logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
27-
for _log in ["uvicorn", "uvicorn.error", "fastapi"]:
28-
_logger = logging.getLogger(_log)
29-
_logger.handlers = [InterceptHandler()]
25+
# logging.basicConfig(handlers=[InterceptHandler()], level=0)
26+
# logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
27+
# for _log in ["uvicorn", "uvicorn.error", "fastapi"]:
28+
# _logger = logging.getLogger(_log)
29+
# _logger.handlers = [InterceptHandler()]
3030

31-
logger.bind(request_id=None, method=None)
31+
# logger.bind(request_id=None, method=None)
3232

3333
return logger
3434

app/core/security.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
from datetime import datetime, timedelta
2-
from typing import Annotated
32

4-
from fastapi import Depends, HTTPException, status
5-
from fastapi.security import OAuth2PasswordBearer
3+
from fastapi import HTTPException, status
64
from jose import ExpiredSignatureError, JWTError, jwt
75
from passlib.context import CryptContext
86
from pydantic import ValidationError
97

10-
import app.models as models
118
from app.core.settings import Settings, get_settings
12-
from app.models.database import AsyncSessionDep
139
from app.schemas import TokenPayload
1410

1511
settings = get_settings()
1612

1713
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
18-
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/tokens")
1914

2015

2116
def decode_token(token: str, settings: Settings) -> TokenPayload:
@@ -63,18 +58,3 @@ def get_password_hash(password: str) -> str:
6358

6459
def verify_password(plain_password: str, hashed_password: str) -> bool:
6560
return pwd_context.verify(plain_password, hashed_password)
66-
67-
68-
async def get_current_user(
69-
db: AsyncSessionDep,
70-
token: Annotated[str, Depends(oauth2_scheme)],
71-
settings: Annotated[str, Depends(get_settings)],
72-
):
73-
token_data = decode_token(token=token, settings=settings)
74-
user = await models.User.get(db=db, id=token_data.sub)
75-
if not user:
76-
raise HTTPException(
77-
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
78-
)
79-
80-
return user

app/models/quiz.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Quiz(Base):
2323
DateTime(timezone=True), server_default=func.now()
2424
)
2525
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
26-
created_by: Mapped[str] = mapped_column(ForeignKey("users.id"))
26+
created_by: Mapped[int] = mapped_column(ForeignKey("users.id"))
2727

2828
# have to use "Question" to avoid circular dependencies
2929
questions: Mapped[list["Question"]] = relationship(

app/models/user.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class User(Base):
2727
created_at: Mapped[datetime] = mapped_column(
2828
DateTime(timezone=True), server_default=func.now()
2929
)
30-
last_login: Mapped[datetime] = mapped_column(DateTime(timezone=True))
3130

3231
# have to use "Quiz" to avoid circular dependencies
3332
quizzes: Mapped[list["Quiz"]] = relationship("Quiz", back_populates="user")

app/schemas/quiz.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class QuizBase(BaseModel):
99
title: str
1010
description: str | None = None
11-
created_by: str | None = None
11+
created_by: int | None = None
1212

1313
model_config = ConfigDict(from_attributes=True)
1414

app/schemas/token.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ class Token(BaseModel):
77

88

99
class TokenPayload(BaseModel):
10-
sub: str | None = None
10+
sub: str

0 commit comments

Comments
 (0)