products.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """
  2. Product Routes Module
  3. This module defines all API endpoints for product management including:
  4. - Fetching products (all users)
  5. - Creating/editing products (permissions >= 1)
  6. - Deleting products (permissions == 2 only)
  7. Permission levels:
  8. - 0: Regular user (read-only access)
  9. - 1: Manager (can create/edit products)
  10. - 2: Admin (full access including delete)
  11. """
  12. # Standard library imports
  13. from math import prod
  14. from logging import getLogger
  15. from typing import Optional
  16. # Third-party imports
  17. from fastapi import APIRouter, Depends, Query
  18. from fastapi.responses import JSONResponse
  19. from h11 import Data
  20. # Local imports
  21. from auth.security import get_current_user
  22. from models import user
  23. from models.items import ProductCreateRequest, ProductEditRequest
  24. from services.data_service import DataServiceFactory
  25. from config.messages import ErrorResponse, SuccessResponse, UserResponse
  26. # Initialize logger for this module
  27. logger = getLogger(__name__)
  28. # Initialize data services for products and users
  29. product_data_service = DataServiceFactory.get_product_service()
  30. user_data_service = DataServiceFactory.get_user_service()
  31. # Create router instance for product-related endpoints
  32. product_router = APIRouter()
  33. @product_router.get("/")
  34. async def get_products(status: Optional[int] = Query(None), current_user = Depends(get_current_user)):
  35. """
  36. Get all products - Available to all authenticated users
  37. Returns:
  38. JSONResponse: List of all products with success message
  39. """
  40. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  41. logger.info("Fetching all products")
  42. # Retrieve all products and convert to dictionary format
  43. all_products = list(map(lambda p: p.model_dump(), product_data_service.get_all()))
  44. if status is not None:
  45. # Filter products by status if provided
  46. all_products = [product for product in all_products if product['status'] == status]
  47. return JSONResponse({"products": all_products, "message": SuccessResponse.PRODUCTS_FETCH_SUCCESS})
  48. @product_router.get("/{product_id}")
  49. async def get_product(product_id: int, current_user = Depends(get_current_user)):
  50. """
  51. Get a specific product by ID - Available to all authenticated users
  52. Args:
  53. product_id (int): The ID of the product to retrieve
  54. current_user: Authenticated user (dependency injection)
  55. Returns:
  56. JSONResponse: Product data if found, error message if not found
  57. """
  58. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  59. logger.info(f"Fetching product with ID: {product_id}")
  60. # Attempt to find product by ID
  61. product = product_data_service.get_by_id(product_id)
  62. if product:
  63. return JSONResponse({"product": product.model_dump(), "message": SuccessResponse.PRODUCTS_FETCH_SUCCESS})
  64. # Return 404 if product not found
  65. return JSONResponse({"message": UserResponse.USER_NOT_FOUND.format(user_id=product_id)}, status_code=404)
  66. # MODERATE RISK OPERATIONS - Requires permissions >= 1 (Manager level or above)
  67. @product_router.patch("/{product_id}/edit")
  68. async def edit_product(product_id: int, product: ProductEditRequest, current_user = Depends(get_current_user)):
  69. """
  70. Edit an existing product - Requires manager permissions (level >= 1)
  71. Args:
  72. product (ProductEditRequest): Product data to update
  73. current_user: Authenticated user (dependency injection)
  74. Returns:
  75. JSONResponse: Updated product data or permission denied message
  76. """
  77. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  78. logger.info(f"Editing product: {product_id}")
  79. # Check if user has sufficient permissions (manager level or above)
  80. if user_data_service.permissions(current_user.id) > 0:
  81. # Update product with provided data (excluding unset fields)
  82. product_data_service.update(product_id, **product.model_dump(exclude_unset=True))
  83. # Retrieve updated product to return in response
  84. edited_product = product_data_service.get_by_id(product_id)
  85. if not edited_product:
  86. return JSONResponse({"message": UserResponse.USER_NOT_FOUND.format(user_id=product_id)}, status_code=404)
  87. logger.info(f"Product {product_id} edited successfully")
  88. return JSONResponse({"message": SuccessResponse.PRODUCT_EDIT_SUCCESS, "product": edited_product.model_dump()})
  89. # Return 403 if user lacks permissions
  90. return JSONResponse({"message": UserResponse.NOT_PERMITTED}, status_code=403)
  91. @product_router.post("/create")
  92. async def create_product(product: ProductCreateRequest, current_user = Depends(get_current_user)):
  93. """
  94. Create a new product - Requires manager permissions (level >= 1)
  95. Args:
  96. product (ProductCreateRequest): New product data
  97. current_user: Authenticated user (dependency injection)
  98. Returns:
  99. JSONResponse: Success message or permission denied message
  100. """
  101. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  102. logger.info("Creating a new product")
  103. # Check if user has sufficient permissions (manager level or above)
  104. if user_data_service.permissions(current_user.id) > 0:
  105. # Create new product with provided data
  106. product_data_service.create(**product.model_dump(exclude_unset=True))
  107. return JSONResponse({"message": SuccessResponse.PRODUCT_CREATE_SUCCESS, "product": product.model_dump()}, status_code=201)
  108. # Return 403 if user lacks permissions
  109. return JSONResponse({"message": UserResponse.NOT_PERMITTED}, status_code=403)
  110. @product_router.patch("/{product_id}/swap-status")
  111. async def switch_product_status(product_id: int, current_user = Depends(get_current_user)):
  112. """
  113. Toggle product status between active/inactive - Requires manager permissions (level >= 1)
  114. Args:
  115. product_id (int): ID of the product to update
  116. status (int): New status value (0=inactive, 1=active)
  117. current_user: Authenticated user (dependency injection)
  118. Returns:
  119. JSONResponse: Success message or permission denied message
  120. """
  121. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  122. logger.info(f"Switching status for product with ID: {product_id}")
  123. # Check if user has sufficient permissions (manager level or above)
  124. if user_data_service.permissions(current_user.id) > 0:
  125. # Update only the status field of the specified product
  126. product = product_data_service.get_by_id(product_id)
  127. if not product:
  128. return JSONResponse({"message": ErrorResponse.PRODDUCT_NOT_FOUND.format(product_id=product_id)}, status_code=404)
  129. status = 0 if product.status == 1 else 1
  130. product_data_service.update(product_id, status=status)
  131. return JSONResponse({"message": SuccessResponse.PRODUCT_EDIT_SUCCESS})
  132. # Return 403 if user lacks permissions
  133. return JSONResponse({"message": UserResponse.NOT_PERMITTED}, status_code=403)
  134. # HIGH RISK OPERATIONS - Requires permissions == 2 (Admin level only)
  135. @product_router.delete("/{product_id}")
  136. async def delete_product(product_id: int, current_user = Depends(get_current_user)):
  137. """
  138. Delete a product permanently - Requires admin permissions (level == 2)
  139. This is a high-risk operation that permanently removes product data.
  140. Only users with admin-level permissions can perform this action.
  141. Args:
  142. product_id (int): ID of the product to delete
  143. current_user: Authenticated user (dependency injection)
  144. Returns:
  145. JSONResponse: Success message or permission denied message
  146. """
  147. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  148. logger.info(f"Deleting product with ID: {product_id}")
  149. # Check if user has admin permissions (exactly level 2)
  150. if user_data_service.permissions(current_user.id) == 2:
  151. # Permanently delete the product
  152. product_data_service.delete(product_id)
  153. return JSONResponse({"message": SuccessResponse.PRODUCT_DELETE_SUCCESS})
  154. # Return 403 if user lacks admin permissions
  155. return JSONResponse({"message": UserResponse.NOT_PERMITTED}, status_code=403)