-
Notifications
You must be signed in to change notification settings - Fork 1
added patch and delete endpoint for category and patch endpoint for transaction.. #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
6ae7971
dc3b490
28e6ad9
edcffb0
c2f23ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,60 @@ | ||
| from fastapi import Request, HTTPException | ||
| from fastapi import Request, HTTPException, Depends | ||
| from fastapi.responses import JSONResponse | ||
| from db import models | ||
| from jwt import decode, DecodeError | ||
| from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials | ||
| from sqlalchemy.orm import Session | ||
| from starlette.middleware.base import BaseHTTPMiddleware | ||
| from db import models | ||
| from db.database import get_db | ||
| from utils.utils import decode_token | ||
| from jwt import DecodeError | ||
|
|
||
| security = HTTPBearer() | ||
|
|
||
| def get_user_from_token(token: str, db: Session): | ||
| try: | ||
| payload = decode_token(token) | ||
| user_id = int(payload.get("sub")) | ||
| if not user_id: | ||
| return None | ||
| except HTTPException: | ||
| return None | ||
| try: | ||
| user = db.query(models.User).filter(models.User.id == user_id).first() | ||
| return user | ||
| except Exception: | ||
| return None | ||
|
|
||
|
|
||
| # Middleware to attach user to request.state | ||
| class AuthMiddleware(BaseHTTPMiddleware): | ||
| async def dispatch(self, request: Request, call_next): | ||
| db = next(get_db()) | ||
| auth_header = request.headers.get("Authorization") | ||
| if auth_header and auth_header.startswith("Bearer "): | ||
| token = auth_header.split(" ")[1] | ||
| user = get_user_from_token(token, db) | ||
| if user: | ||
| request.state.user = user | ||
| else: | ||
| return JSONResponse({"detail": "Invalid token"}, status_code=401) | ||
| else: | ||
| request.state.user = None | ||
| try: | ||
| auth_header = request.headers.get("Authorization") | ||
|
|
||
| if auth_header and auth_header.startswith("Bearer "): | ||
| token = auth_header.split(" ")[1] | ||
| user = get_user_from_token(token, db) | ||
| if user: | ||
| request.state.user = user | ||
| print("USER:", request.state.user) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this line |
||
|
|
||
| else: | ||
| return JSONResponse({"message": "Invalid token"}, status_code=401) | ||
| else: | ||
| request.state.user = None | ||
|
|
||
| response = await call_next(request) | ||
| return response | ||
| finally: | ||
| db.close() | ||
| db.close() | ||
|
|
||
|
|
||
| # Helper function to decode token and fetch user | ||
| def get_user_from_token(token: str, db: Session): | ||
| try: | ||
| payload = decode_token(token) | ||
| user_id = int(payload.get("sub")) # just to ensure integer | ||
| if not user_id: | ||
| raise HTTPException(status_code=401, detail="Invalid token payload") | ||
|
|
||
| except (KeyError, ValueError, DecodeError): | ||
| raise HTTPException(status_code=401, detail="Invalid authentication token") | ||
|
|
||
| return db.query(models.User).filter(models.User.id == user_id).first() | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle error for when no user is found |
||
|
|
||
|
|
||
| # Dependency to protect routes | ||
| def require_auth( | ||
| request: Request, | ||
| credentials: HTTPAuthorizationCredentials = Depends(security), | ||
| ) -> models.User: | ||
| user = getattr(request.state, "user", None) | ||
| if not user: | ||
| raise HTTPException(status_code=401, detail="Not authenticated") | ||
| return user | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,6 @@ | |
| CreateUser, | ||
| Login, | ||
| UserResponse, | ||
| AllUsers, | ||
| RefreshTokenRequest, | ||
| AccessTokenResponse, | ||
| ) | ||
|
|
@@ -89,12 +88,12 @@ def get_user( | |
| return request.state.user | ||
|
|
||
|
|
||
| @router.get("/users", response_model=AllUsers, status_code=status.HTTP_200_OK) | ||
| @router.get("/users", response_model=list[UserResponse], status_code=status.HTTP_200_OK) | ||
| def get_AllUsers( | ||
| db: Session = Depends(get_db), | ||
| ): | ||
| users = db.query(models.User).all() | ||
| return {"users": users} | ||
| return users | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return in a json and proper format |
||
|
|
||
|
|
||
| @router.post("/seed_me", status_code=status.HTTP_200_OK) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,77 +1,140 @@ | ||
| from fastapi import APIRouter, Depends, Request, HTTPException, status | ||
| from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials | ||
| from fastapi import APIRouter, Depends, HTTPException, status | ||
| from fastapi.security import HTTPBearer | ||
| from sqlalchemy.orm import Session | ||
| from db.database import get_db | ||
| from schemas.schemas import Category, AllCategories | ||
| from db import models | ||
| from schemas.schemas import Transaction, CategoryTransactionResponse | ||
| from db.database import get_db | ||
| from schemas.schemas import CategoryResponse,Category, Categoryupdate, TransactionResponse | ||
| from typing import List | ||
| from middlewares.authMiddleWare import require_auth | ||
|
|
||
| router = APIRouter() | ||
| security = HTTPBearer() | ||
| security = HTTPBearer() | ||
|
|
||
|
|
||
| @router.post("/categories") | ||
| @router.post("/categories", status_code=status.HTTP_201_CREATED) | ||
| def add_category( | ||
| request: Request, | ||
| category: Category, | ||
| db: Session = Depends(get_db), | ||
| credentials: HTTPAuthorizationCredentials = Depends(security), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| user_id = request.state.user.id | ||
| user = db.query(models.User).filter(models.User.id == user_id).first() | ||
| if not user: | ||
| raise HTTPException(status_code=404, detail="User not found") | ||
| normalized_name = category.name.strip().lower() | ||
| existing_category = db.query(models.Category).filter( | ||
| models.Category.user_id == current_user.id, | ||
| models.Category.name.ilike(normalized_name) | ||
| ).first() | ||
| if existing_category: | ||
| raise HTTPException(status_code=400, detail="Category with this name already exists") | ||
|
|
||
| new_category = models.Category( | ||
| name=category.name, user_id=user.id, color=category.color, icon=category.icon | ||
| name=normalized_name, | ||
| color=category.color, | ||
| icon=category.icon, | ||
| user_id=current_user.id | ||
| ) | ||
| db.add(new_category) | ||
| db.commit() | ||
| db.refresh(new_category) | ||
| return {"id": new_category.id, "message": "New category added"} | ||
| return {"id": new_category.id, "message": "Category added successfully"} | ||
|
|
||
|
|
||
| @router.get( | ||
| "/categories", response_model=AllCategories, status_code=status.HTTP_200_OK | ||
| ) | ||
| @router.get("/categories", response_model=list[CategoryResponse], status_code=status.HTTP_200_OK) | ||
| def get_categories( | ||
| request: Request, | ||
| db: Session = Depends(get_db), | ||
| credentials: HTTPAuthorizationCredentials = Depends(security), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| categories = db.query(models.Category).filter(models.Category.user_id == current_user.id).all() | ||
| return categories | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return in a proper json format |
||
|
|
||
|
|
||
| @router.get("/categories/{id}", status_code=status.HTTP_200_OK) | ||
| def get_category( | ||
| id: int, | ||
| db: Session = Depends(get_db), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| user_id = request.state.user.id | ||
| categories = db.query(models.Category).filter(models.Category.user_id == user_id).all() | ||
| categories = db.query(models.Category).filter( | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a single category... rename it to category |
||
| models.Category.id == id, | ||
| models.Category.user_id == current_user.id | ||
| ).first() | ||
| if not categories: | ||
| raise HTTPException(status_code=404, detail="Categories not found") | ||
|
|
||
| return {"categories": categories} | ||
| raise HTTPException(status_code=404, detail="Category not found") | ||
| return categories | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use proper json format |
||
|
|
||
|
|
||
| @router.get("/category/{id}/transactions",response_model=CategoryTransactionResponse, status_code=status.HTTP_200_OK) | ||
| def category_transactions(id:int, db: Session = Depends(get_db)): | ||
| category = db.query(models.Category).filter(models.Category.id == id).first() | ||
| @router.get("/categories/{id}/transactions", response_model=List[TransactionResponse], status_code=status.HTTP_200_OK) | ||
| def category_transactions( | ||
| id: int, | ||
| db: Session = Depends(get_db), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| category = db.query(models.Category).filter( | ||
| models.Category.id == id, | ||
| models.Category.user_id == current_user.id | ||
| ).first() | ||
| if not category: | ||
| raise HTTPException(status_code=404, detail="Category not found") | ||
| transactions = db.query(models.Transaction).filter(models.Transaction.category_id == id).all() | ||
| return {'transactions': transactions } | ||
|
|
||
| transactions = db.query(models.Transaction).filter( | ||
| models.Transaction.category_id == id, | ||
| models.Transaction.user_id == current_user.id | ||
| ).all() | ||
| return {"transactions": transactions} | ||
|
|
||
|
|
||
| @router.get("/category/{id}", status_code=status.HTTP_200_OK) | ||
| def getCategory(id: int, db: Session = Depends(get_db)): | ||
| category = db.query(models.Category).filter(models.Category.id == id).first() | ||
| @router.patch("/categories/{category_id}") | ||
| def update_category( | ||
| category_id: int, | ||
| payload: Categoryupdate, | ||
| db: Session = Depends(get_db), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| category = db.query(models.Category).filter( | ||
| models.Category.id == category_id, | ||
| models.Category.user_id == current_user.id | ||
| ).first() | ||
| if not category: | ||
| raise HTTPException(status_code=404, detail="Category not found") | ||
| return category | ||
|
|
||
| @router.delete("/category/{id}", status_code=status.HTTP_200_OK) | ||
| def deleteCategory(id: int, db: Session = Depends(get_db)): | ||
| category_to_delete = db.query(models.Category).filter(models.Category.id == id).first() | ||
| update_data = payload.model_dump(exclude_unset=True) | ||
| if not update_data: | ||
| raise HTTPException(status_code=400, detail="No fields provided for update") | ||
|
|
||
| for field, value in update_data.items(): | ||
| if isinstance(value, str) and not value.strip(): | ||
| raise HTTPException(status_code=400, detail=f"{field} cannot be empty") | ||
| setattr(category, field, value) | ||
|
|
||
| db.commit() | ||
| db.refresh(category) | ||
| return {"message": "Category updated successfully", "category_id": category.id} | ||
|
|
||
|
|
||
| if not category_to_delete: | ||
| @router.delete("/categories/{category_id}") | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe related transaction should be deleted first |
||
| def delete_category( | ||
| category_id: int, | ||
| db: Session = Depends(get_db), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| category = db.query(models.Category).filter( | ||
| models.Category.id == category_id, | ||
| models.Category.user_id == current_user.id | ||
| ).first() | ||
| if not category: | ||
| raise HTTPException(status_code=404, detail="Category not found") | ||
|
|
||
| db.delete(category_to_delete) | ||
| db.delete(category) | ||
| db.commit() | ||
| return { | ||
| "message": "Category deleted successfully", | ||
| } | ||
| return {"message": "Category deleted successfully"} | ||
|
|
||
|
|
||
| @router.delete("/categories") | ||
| def delete_all_categories( | ||
| db: Session = Depends(get_db), | ||
| current_user: models.User = Depends(require_auth), | ||
| ): | ||
| # Delete all transactions first | ||
| db.query(models.Transaction).filter(models.Transaction.user_id == current_user.id).delete(synchronize_session=False) | ||
| # Delete all categories | ||
| db.query(models.Category).filter(models.Category.user_id == current_user.id).delete(synchronize_session=False) | ||
| db.commit() | ||
| return {"message": "All categories and related transactions deleted"} | ||
Uh oh!
There was an error while loading. Please reload this page.