Skip to content

Commit c502e7a

Browse files
committed
better exception handling
1 parent 0d307c7 commit c502e7a

File tree

12 files changed

+106
-105
lines changed

12 files changed

+106
-105
lines changed

src/app/api/dependencies.py

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Request
1212
)
1313

14-
from app.api.exceptions import credentials_exception, privileges_exception
14+
from app.core.exceptions.http_exceptions import UnauthorizedException, ForbiddenException, RateLimitException
1515
from app.core.db.database import async_get_db
1616
from app.core.logger import logging
1717
from app.core.schemas import TokenData
@@ -28,38 +28,13 @@
2828
DEFAULT_LIMIT = settings.DEFAULT_RATE_LIMIT_LIMIT
2929
DEFAULT_PERIOD = settings.DEFAULT_RATE_LIMIT_PERIOD
3030

31-
async def get_current_user(
32-
token: Annotated[str, Depends(oauth2_scheme)],
33-
db: Annotated[AsyncSession, Depends(async_get_db)]
34-
) -> dict:
35-
try:
36-
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
37-
username_or_email: str = payload.get("sub")
38-
if username_or_email is None:
39-
raise credentials_exception
40-
token_data = TokenData(username_or_email=username_or_email)
41-
42-
except JWTError:
43-
raise credentials_exception
44-
45-
if "@" in username_or_email:
46-
user = await crud_users.get(db=db, email=token_data.username_or_email)
47-
else:
48-
user = await crud_users.get(db=db, username=token_data.username_or_email)
49-
50-
if user and not user["is_deleted"]:
51-
return user
52-
53-
raise credentials_exception
54-
55-
5631
async def get_current_user(
5732
token: Annotated[str, Depends(oauth2_scheme)],
5833
db: Annotated[AsyncSession, Depends(async_get_db)]
5934
) -> dict:
6035
token_data = await verify_token(token, db)
6136
if token_data is None:
62-
raise credentials_exception
37+
raise UnauthorizedException("User not authenticated.")
6338

6439
if "@" in token_data.username_or_email:
6540
user = await crud_users.get(db=db, email=token_data.username_or_email, is_deleted=False)
@@ -69,7 +44,7 @@ async def get_current_user(
6944
if user:
7045
return user
7146

72-
raise credentials_exception
47+
raise UnauthorizedException("User not authenticated.")
7348

7449

7550
async def get_optional_user(
@@ -103,7 +78,7 @@ async def get_optional_user(
10378

10479
async def get_current_superuser(current_user: Annotated[User, Depends(get_current_user)]) -> dict:
10580
if not current_user["is_superuser"]:
106-
raise privileges_exception
81+
raise ForbiddenException("You do not have enough privileges.")
10782

10883
return current_user
10984

@@ -143,7 +118,4 @@ async def rate_limiter(
143118
period=period
144119
)
145120
if is_limited:
146-
raise HTTPException(
147-
status_code=429,
148-
detail="Rate limit exceeded"
149-
)
121+
raise RateLimitException("Rate limit exceeded.")

src/app/api/exceptions.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/app/api/v1/login.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from app.core.db.database import async_get_db
1010
from app.core.schemas import Token
1111
from app.core.security import ACCESS_TOKEN_EXPIRE_MINUTES, create_access_token, authenticate_user
12-
from app.api.exceptions import credentials_exception
12+
from app.core.exceptions.http_exceptions import UnauthorizedException
1313

1414
router = fastapi.APIRouter(tags=["login"])
1515

@@ -24,7 +24,7 @@ async def login_for_access_token(
2424
db=db
2525
)
2626
if not user:
27-
raise credentials_exception
27+
raise UnauthorizedException("Wrong username, email or password.")
2828

2929
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
3030
access_token = await create_access_token(

src/app/api/v1/logout.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from datetime import datetime
22

3-
from fastapi import APIRouter, Depends, HTTPException, status
3+
from fastapi import APIRouter, Depends, status
44
from sqlalchemy.ext.asyncio import AsyncSession
55
from jose import jwt, JWTError
66

77
from app.core.security import oauth2_scheme, SECRET_KEY, ALGORITHM
88
from app.core.db.database import async_get_db
99
from app.core.db.crud_token_blacklist import crud_token_blacklist
1010
from app.core.schemas import TokenBlacklistCreate
11+
from app.core.exceptions.http_exceptions import UnauthorizedException
1112

1213
router = APIRouter(tags=["login"])
1314

@@ -28,8 +29,4 @@ async def logout(
2829
return {"message": "Logged out successfully"}
2930

3031
except JWTError:
31-
raise HTTPException(
32-
status_code=status.HTTP_401_UNAUTHORIZED,
33-
detail="Invalid token",
34-
headers={"WWW-Authenticate": "Bearer"},
35-
)
32+
raise UnauthorizedException("Invalid token.")

src/app/api/v1/posts.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Annotated
22

3-
from fastapi import Request, Depends, HTTPException
3+
from fastapi import Request, Depends
44
from sqlalchemy.ext.asyncio import AsyncSession
55
import fastapi
66

@@ -10,7 +10,7 @@
1010
from app.core.db.database import async_get_db
1111
from app.crud.crud_posts import crud_posts
1212
from app.crud.crud_users import crud_users
13-
from app.api.exceptions import privileges_exception
13+
from app.core.exceptions.http_exceptions import NotFoundException, ForbiddenException
1414
from app.core.utils.cache import cache
1515
from app.api.paginated import PaginatedListResponse, paginated_response, compute_offset
1616

@@ -26,10 +26,10 @@ async def write_post(
2626
):
2727
db_user = await crud_users.get(db=db, schema_to_select=UserRead, username=username, is_deleted=False)
2828
if db_user is None:
29-
raise HTTPException(status_code=404, detail="User not found")
29+
raise NotFoundException("User not found")
3030

3131
if current_user["id"] != db_user["id"]:
32-
raise privileges_exception
32+
raise ForbiddenException()
3333

3434
post_internal_dict = post.model_dump()
3535
post_internal_dict["created_by_user_id"] = db_user["id"]
@@ -53,7 +53,7 @@ async def read_posts(
5353
):
5454
db_user = await crud_users.get(db=db, schema_to_select=UserRead, username=username, is_deleted=False)
5555
if not db_user:
56-
raise HTTPException(status_code=404, detail="User not found")
56+
raise NotFoundException("User not found")
5757

5858
posts_data = await crud_posts.get_multi(
5959
db=db,
@@ -81,11 +81,11 @@ async def read_post(
8181
):
8282
db_user = await crud_users.get(db=db, schema_to_select=UserRead, username=username, is_deleted=False)
8383
if db_user is None:
84-
raise HTTPException(status_code=404, detail="User not found")
84+
raise NotFoundException("User not found")
8585

8686
db_post = await crud_posts.get(db=db, schema_to_select=PostRead, id=id, created_by_user_id=db_user["id"], is_deleted=False)
8787
if db_post is None:
88-
raise HTTPException(status_code=404, detail="Post not found")
88+
raise NotFoundException("Post not found")
8989

9090
return db_post
9191

@@ -106,14 +106,14 @@ async def patch_post(
106106
):
107107
db_user = await crud_users.get(db=db, schema_to_select=UserRead, username=username, is_deleted=False)
108108
if db_user is None:
109-
raise HTTPException(status_code=404, detail="User not found")
109+
raise NotFoundException("User not found")
110110

111111
if current_user["id"] != db_user["id"]:
112-
raise privileges_exception
112+
raise ForbiddenException()
113113

114114
db_post = await crud_posts.get(db=db, schema_to_select=PostRead, id=id, is_deleted=False)
115115
if db_post is None:
116-
raise HTTPException(status_code=404, detail="Post not found")
116+
raise NotFoundException("Post not found")
117117

118118
await crud_posts.update(db=db, object=values, id=id)
119119
return {"message": "Post updated"}
@@ -134,14 +134,14 @@ async def erase_post(
134134
):
135135
db_user = await crud_users.get(db=db, schema_to_select=UserRead, username=username, is_deleted=False)
136136
if db_user is None:
137-
raise HTTPException(status_code=404, detail="User not found")
137+
raise NotFoundException("User not found")
138138

139139
if current_user["id"] != db_user["id"]:
140-
raise privileges_exception
140+
raise ForbiddenException()
141141

142142
db_post = await crud_posts.get(db=db, schema_to_select=PostRead, id=id, is_deleted=False)
143143
if db_post is None:
144-
raise HTTPException(status_code=404, detail="Post not found")
144+
raise NotFoundException("Post not found")
145145

146146
await crud_posts.delete(db=db, db_row=db_post, id=id)
147147

@@ -162,11 +162,11 @@ async def erase_db_post(
162162
):
163163
db_user = await crud_users.get(db=db, schema_to_select=UserRead, username=username, is_deleted=False)
164164
if db_user is None:
165-
raise HTTPException(status_code=404, detail="User not found")
165+
raise NotFoundException("User not found")
166166

167167
db_post = await crud_posts.get(db=db, schema_to_select=PostRead, id=id, is_deleted=False)
168168
if db_post is None:
169-
raise HTTPException(status_code=404, detail="Post not found")
169+
raise NotFoundException("Post not found")
170170

171171
await crud_posts.db_delete(db=db, id=id)
172172
return {"message": "Post deleted from the database"}

src/app/api/v1/rate_limits.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from app.api.dependencies import get_current_superuser
88
from app.api.paginated import PaginatedListResponse, paginated_response, compute_offset
99
from app.core.db.database import async_get_db
10+
from app.core.exceptions.http_exceptions import NotFoundException, DuplicateValueException, RateLimitException
1011
from app.crud.crud_rate_limit import crud_rate_limits
1112
from app.crud.crud_tier import crud_tiers
1213
from app.schemas.rate_limit import (
@@ -27,14 +28,14 @@ async def write_rate_limit(
2728
):
2829
db_tier = await crud_tiers.get(db=db, name=tier_name)
2930
if not db_tier:
30-
raise HTTPException(status_code=404, detail="Tier not found")
31+
raise NotFoundException("Tier not found")
3132

3233
rate_limit_internal_dict = rate_limit.model_dump()
3334
rate_limit_internal_dict["tier_id"] = db_tier["id"]
3435

3536
db_rate_limit = await crud_rate_limits.exists(db=db, name=rate_limit_internal_dict["name"])
3637
if db_rate_limit:
37-
raise HTTPException(status_code=400, detail="Rate Limit Name not available")
38+
raise DuplicateValueException("Rate Limit Name not available")
3839

3940
rate_limit_internal = RateLimitCreateInternal(**rate_limit_internal_dict)
4041
return await crud_rate_limits.create(db=db, object=rate_limit_internal)
@@ -50,7 +51,7 @@ async def read_rate_limits(
5051
):
5152
db_tier = await crud_tiers.get(db=db, name=tier_name)
5253
if not db_tier:
53-
raise HTTPException(status_code=404, detail="Tier not found")
54+
raise NotFoundException("Tier not found")
5455

5556
rate_limits_data = await crud_rate_limits.get_multi(
5657
db=db,
@@ -76,7 +77,7 @@ async def read_rate_limit(
7677
):
7778
db_tier = await crud_tiers.get(db=db, name=tier_name)
7879
if not db_tier:
79-
raise HTTPException(status_code=404, detail="Tier not found")
80+
raise NotFoundException("Tier not found")
8081

8182
db_rate_limit = await crud_rate_limits.get(
8283
db=db,
@@ -85,7 +86,7 @@ async def read_rate_limit(
8586
id=id
8687
)
8788
if db_rate_limit is None:
88-
raise HTTPException(status_code=404, detail="Rate Limit not found")
89+
raise NotFoundException("Rate Limit not found")
8990

9091
return db_rate_limit
9192

@@ -100,7 +101,7 @@ async def patch_rate_limit(
100101
):
101102
db_tier = await crud_tiers.get(db=db, name=tier_name)
102103
if db_tier is None:
103-
raise HTTPException(status_code=404, detail="Tier not found")
104+
raise NotFoundException("Tier not found")
104105

105106
db_rate_limit = await crud_rate_limits.get(
106107
db=db,
@@ -109,19 +110,19 @@ async def patch_rate_limit(
109110
id=id
110111
)
111112
if db_rate_limit is None:
112-
raise HTTPException(status_code=404, detail="Rate Limit not found")
113+
raise NotFoundException("Rate Limit not found")
113114

114115
db_rate_limit_path = await crud_rate_limits.exists(
115116
db=db,
116117
tier_id=db_tier["id"],
117118
path=values.path
118119
)
119120
if db_rate_limit_path is not None:
120-
raise HTTPException(status_code=404, detail="There is already a rate limit for this path")
121+
raise DuplicateValueException("There is already a rate limit for this path")
121122

122123
db_rate_limit_name = await crud_rate_limits.exists(db=db)
123124
if db_rate_limit_path is not None:
124-
raise HTTPException(status_code=404, detail="There is already a rate limit with this name")
125+
raise DuplicateValueException("There is already a rate limit with this name")
125126

126127
await crud_rate_limits.update(db=db, object=values, id=db_rate_limit["id"])
127128
return {"message": "Rate Limit updated"}
@@ -136,7 +137,7 @@ async def erase_rate_limit(
136137
):
137138
db_tier = await crud_tiers.get(db=db, name=tier_name)
138139
if not db_tier:
139-
raise HTTPException(status_code=404, detail="Tier not found")
140+
raise NotFoundException("Tier not found")
140141

141142
db_rate_limit = await crud_rate_limits.get(
142143
db=db,
@@ -145,7 +146,7 @@ async def erase_rate_limit(
145146
id=id
146147
)
147148
if db_rate_limit is None:
148-
raise HTTPException(status_code=404, detail="Rate Limit not found")
149+
raise RateLimitException("Rate Limit not found")
149150

150151
await crud_rate_limits.delete(db=db, db_row=db_rate_limit, id=db_rate_limit["id"])
151152
return {"message": "Rate Limit deleted"}

src/app/api/v1/tiers.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
)
1313
from app.api.dependencies import get_current_superuser
1414
from app.core.db.database import async_get_db
15+
from app.core.exceptions.http_exceptions import DuplicateValueException, NotFoundException
1516
from app.crud.crud_tier import crud_tiers
1617
from app.api.paginated import PaginatedListResponse, paginated_response, compute_offset
1718

@@ -26,7 +27,7 @@ async def write_tier(
2627
tier_internal_dict = tier.model_dump()
2728
db_tier = await crud_tiers.exists(db=db, name=tier_internal_dict["name"])
2829
if db_tier:
29-
raise HTTPException(status_code=400, detail="Tier Name not available")
30+
raise DuplicateValueException("Tier Name not available")
3031

3132
tier_internal = TierCreateInternal(**tier_internal_dict)
3233
return await crud_tiers.create(db=db, object=tier_internal)
@@ -61,7 +62,7 @@ async def read_tier(
6162
):
6263
db_tier = await crud_tiers.get(db=db, schema_to_select=TierRead, name=name)
6364
if db_tier is None:
64-
raise HTTPException(status_code=404, detail="Tier not found")
65+
raise NotFoundException("Tier not found")
6566

6667
return db_tier
6768

@@ -75,7 +76,7 @@ async def patch_tier(
7576
):
7677
db_tier = await crud_tiers.get(db=db, schema_to_select=TierRead, name=name)
7778
if db_tier is None:
78-
raise HTTPException(status_code=404, detail="Tier not found")
79+
raise NotFoundException("Tier not found")
7980

8081
await crud_tiers.update(db=db, object=values, name=name)
8182
return {"message": "Tier updated"}
@@ -89,7 +90,7 @@ async def erase_tier(
8990
):
9091
db_tier = await crud_tiers.get(db=db, schema_to_select=TierRead, name=name)
9192
if db_tier is None:
92-
raise HTTPException(status_code=404, detail="Tier not found")
93+
raise NotFoundException("Tier not found")
9394

9495
await crud_tiers.delete(db=db, db_row=db_tier, name=name)
9596
return {"message": "Tier deleted"}

0 commit comments

Comments
 (0)