Main exception renamed to WizamartException
This commit is contained in:
@@ -9,30 +9,20 @@ These endpoints allow admins to:
|
|||||||
- Reset themes to default
|
- Reset themes to default
|
||||||
|
|
||||||
All operations use the service layer for business logic.
|
All operations use the service layer for business logic.
|
||||||
|
All exceptions are handled by the global exception handler.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Path, status
|
from fastapi import APIRouter, Depends, Path
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.api.deps import get_current_admin_user, get_db
|
from app.api.deps import get_current_admin_user, get_db
|
||||||
from app.services.vendor_theme_service import vendor_theme_service
|
from app.services.vendor_theme_service import vendor_theme_service
|
||||||
from app.exceptions.vendor import VendorNotFoundException
|
|
||||||
from app.exceptions.vendor_theme import (
|
|
||||||
VendorThemeNotFoundException,
|
|
||||||
ThemePresetNotFoundException,
|
|
||||||
ThemeValidationException,
|
|
||||||
ThemeOperationException,
|
|
||||||
InvalidColorFormatException,
|
|
||||||
InvalidFontFamilyException
|
|
||||||
)
|
|
||||||
from models.database.user import User
|
from models.database.user import User
|
||||||
from models.schema.vendor_theme import (
|
from models.schema.vendor_theme import (
|
||||||
VendorThemeResponse,
|
VendorThemeResponse,
|
||||||
VendorThemeUpdate,
|
VendorThemeUpdate,
|
||||||
ThemePresetResponse,
|
|
||||||
ThemePresetListResponse
|
ThemePresetListResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,16 +51,8 @@ async def get_theme_presets(
|
|||||||
"""
|
"""
|
||||||
logger.info("Getting theme presets")
|
logger.info("Getting theme presets")
|
||||||
|
|
||||||
try:
|
presets = vendor_theme_service.get_available_presets()
|
||||||
presets = vendor_theme_service.get_available_presets()
|
return {"presets": presets}
|
||||||
return {"presets": presets}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get theme presets: {e}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail="Failed to retrieve theme presets"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -97,27 +79,14 @@ async def get_vendor_theme(
|
|||||||
- Complete theme configuration including colors, fonts, layout, and branding
|
- Complete theme configuration including colors, fonts, layout, and branding
|
||||||
|
|
||||||
**Errors:**
|
**Errors:**
|
||||||
- `404`: Vendor not found
|
- `404`: Vendor not found (VendorNotFoundException)
|
||||||
- `500`: Internal server error
|
|
||||||
"""
|
"""
|
||||||
logger.info(f"Getting theme for vendor: {vendor_code}")
|
logger.info(f"Getting theme for vendor: {vendor_code}")
|
||||||
|
|
||||||
try:
|
# Service raises VendorNotFoundException if vendor not found
|
||||||
theme = vendor_theme_service.get_theme(db, vendor_code)
|
# Global exception handler converts it to HTTP 404
|
||||||
return theme
|
theme = vendor_theme_service.get_theme(db, vendor_code)
|
||||||
|
return theme
|
||||||
except VendorNotFoundException:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"Vendor '{vendor_code}' not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get theme for vendor {vendor_code}: {e}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail="Failed to retrieve theme"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -155,40 +124,16 @@ async def update_vendor_theme(
|
|||||||
- Updated theme configuration
|
- Updated theme configuration
|
||||||
|
|
||||||
**Errors:**
|
**Errors:**
|
||||||
- `404`: Vendor not found
|
- `404`: Vendor not found (VendorNotFoundException)
|
||||||
- `422`: Validation error (invalid colors, fonts, or layout values)
|
- `422`: Validation error (ThemeValidationException, InvalidColorFormatException, etc.)
|
||||||
- `500`: Internal server error
|
- `500`: Operation failed (ThemeOperationException)
|
||||||
"""
|
"""
|
||||||
logger.info(f"Updating theme for vendor: {vendor_code}")
|
logger.info(f"Updating theme for vendor: {vendor_code}")
|
||||||
|
|
||||||
try:
|
# Service handles all validation and raises appropriate exceptions
|
||||||
theme = vendor_theme_service.update_theme(db, vendor_code, theme_data)
|
# Global exception handler converts them to proper HTTP responses
|
||||||
return theme.to_dict()
|
theme = vendor_theme_service.update_theme(db, vendor_code, theme_data)
|
||||||
|
return theme.to_dict()
|
||||||
except VendorNotFoundException:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"Vendor '{vendor_code}' not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
except (ThemeValidationException, InvalidColorFormatException, InvalidFontFamilyException) as e:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
||||||
detail=e.message
|
|
||||||
)
|
|
||||||
|
|
||||||
except ThemeOperationException as e:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail=e.message
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to update theme for vendor {vendor_code}: {e}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail="Failed to update theme"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -227,43 +172,20 @@ async def apply_theme_preset(
|
|||||||
- Success message and applied theme configuration
|
- Success message and applied theme configuration
|
||||||
|
|
||||||
**Errors:**
|
**Errors:**
|
||||||
- `404`: Vendor or preset not found
|
- `404`: Vendor not found (VendorNotFoundException) or preset not found (ThemePresetNotFoundException)
|
||||||
- `500`: Internal server error
|
- `500`: Operation failed (ThemeOperationException)
|
||||||
"""
|
"""
|
||||||
logger.info(f"Applying preset '{preset_name}' to vendor {vendor_code}")
|
logger.info(f"Applying preset '{preset_name}' to vendor {vendor_code}")
|
||||||
|
|
||||||
try:
|
# Service validates preset name and applies it
|
||||||
theme = vendor_theme_service.apply_theme_preset(db, vendor_code, preset_name)
|
# Raises ThemePresetNotFoundException if preset doesn't exist
|
||||||
|
# Global exception handler converts to HTTP 404
|
||||||
|
theme = vendor_theme_service.apply_theme_preset(db, vendor_code, preset_name)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"message": f"Applied {preset_name} preset successfully",
|
"message": f"Applied {preset_name} preset successfully",
|
||||||
"theme": theme.to_dict()
|
"theme": theme.to_dict()
|
||||||
}
|
}
|
||||||
|
|
||||||
except VendorNotFoundException:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"Vendor '{vendor_code}' not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
except ThemePresetNotFoundException as e:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=e.message
|
|
||||||
)
|
|
||||||
|
|
||||||
except ThemeOperationException as e:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail=e.message
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to apply preset to vendor {vendor_code}: {e}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail="Failed to apply preset"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -291,36 +213,12 @@ async def delete_vendor_theme(
|
|||||||
- Success message
|
- Success message
|
||||||
|
|
||||||
**Errors:**
|
**Errors:**
|
||||||
- `404`: Vendor not found or vendor has no custom theme
|
- `404`: Vendor not found (VendorNotFoundException) or no custom theme (VendorThemeNotFoundException)
|
||||||
- `500`: Internal server error
|
- `500`: Operation failed (ThemeOperationException)
|
||||||
"""
|
"""
|
||||||
logger.info(f"Deleting theme for vendor: {vendor_code}")
|
logger.info(f"Deleting theme for vendor: {vendor_code}")
|
||||||
|
|
||||||
try:
|
# Service handles deletion and raises exceptions if needed
|
||||||
result = vendor_theme_service.delete_theme(db, vendor_code)
|
# Global exception handler converts them to proper HTTP responses
|
||||||
return result
|
result = vendor_theme_service.delete_theme(db, vendor_code)
|
||||||
|
return result
|
||||||
except VendorNotFoundException:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"Vendor '{vendor_code}' not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
except VendorThemeNotFoundException:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"Vendor '{vendor_code}' has no custom theme"
|
|
||||||
)
|
|
||||||
|
|
||||||
except ThemeOperationException as e:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail=e.message
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to delete theme for vendor {vendor_code}: {e}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail="Failed to delete theme"
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ Vendor management endpoints for admin.
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query, Path, HTTPException, Body
|
from fastapi import APIRouter, Depends, Query, Path, Body
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.api.deps import get_current_admin_user
|
from app.api.deps import get_current_admin_user
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.services.admin_service import admin_service
|
from app.services.admin_service import admin_service
|
||||||
from app.services.stats_service import stats_service
|
from app.services.stats_service import stats_service
|
||||||
from app.exceptions import VendorNotFoundException
|
from app.exceptions import VendorNotFoundException, ConfirmationRequiredException
|
||||||
from models.schema.stats import VendorStatsResponse
|
from models.schema.stats import VendorStatsResponse
|
||||||
from models.schema.vendor import (
|
from models.schema.vendor import (
|
||||||
VendorListResponse,
|
VendorListResponse,
|
||||||
@@ -432,12 +432,13 @@ def delete_vendor(
|
|||||||
|
|
||||||
Requires confirmation parameter: `confirm=true`
|
Requires confirmation parameter: `confirm=true`
|
||||||
"""
|
"""
|
||||||
|
# Raise custom exception instead of HTTPException
|
||||||
if not confirm:
|
if not confirm:
|
||||||
raise HTTPException(
|
raise ConfirmationRequiredException(
|
||||||
status_code=400,
|
operation="delete_vendor",
|
||||||
detail="Deletion requires confirmation parameter: confirm=true"
|
message="Deletion requires confirmation parameter: confirm=true"
|
||||||
)
|
)
|
||||||
|
|
||||||
vendor = _get_vendor_by_identifier(db, vendor_identifier)
|
vendor = _get_vendor_by_identifier(db, vendor_identifier)
|
||||||
message = admin_service.delete_vendor(db, vendor.id)
|
message = admin_service.delete_vendor(db, vendor.id)
|
||||||
return {"message": message}
|
return {"message": message}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ messages, and HTTP status mappings.
|
|||||||
|
|
||||||
# Base exceptions
|
# Base exceptions
|
||||||
from .base import (
|
from .base import (
|
||||||
LetzShopException,
|
WizamartException,
|
||||||
ValidationException,
|
ValidationException,
|
||||||
AuthenticationException,
|
AuthenticationException,
|
||||||
AuthorizationException,
|
AuthorizationException,
|
||||||
@@ -41,6 +41,7 @@ from .admin import (
|
|||||||
CannotModifySelfException,
|
CannotModifySelfException,
|
||||||
InvalidAdminActionException,
|
InvalidAdminActionException,
|
||||||
BulkOperationException,
|
BulkOperationException,
|
||||||
|
ConfirmationRequiredException,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Marketplace import job exceptions
|
# Marketplace import job exceptions
|
||||||
@@ -170,7 +171,7 @@ from .order import (
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Base exceptions
|
# Base exceptions
|
||||||
"LetzShopException",
|
"WizamartException",
|
||||||
"ValidationException",
|
"ValidationException",
|
||||||
"AuthenticationException",
|
"AuthenticationException",
|
||||||
"AuthorizationException",
|
"AuthorizationException",
|
||||||
@@ -304,4 +305,5 @@ __all__ = [
|
|||||||
"CannotModifySelfException",
|
"CannotModifySelfException",
|
||||||
"InvalidAdminActionException",
|
"InvalidAdminActionException",
|
||||||
"BulkOperationException",
|
"BulkOperationException",
|
||||||
|
"ConfirmationRequiredException",
|
||||||
]
|
]
|
||||||
@@ -57,17 +57,17 @@ class UserStatusChangeException(BusinessLogicException):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VendorVerificationException(BusinessLogicException):
|
class ShopVerificationException(BusinessLogicException):
|
||||||
"""Raised when vendor verification fails."""
|
"""Raised when shop verification fails."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
vendor_id: int,
|
shop_id: int,
|
||||||
reason: str,
|
reason: str,
|
||||||
current_verification_status: Optional[bool] = None,
|
current_verification_status: Optional[bool] = None,
|
||||||
):
|
):
|
||||||
details = {
|
details = {
|
||||||
"vendor_id": vendor_id,
|
"shop_id": shop_id,
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +75,8 @@ class VendorVerificationException(BusinessLogicException):
|
|||||||
details["current_verification_status"] = current_verification_status
|
details["current_verification_status"] = current_verification_status
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
message=f"Vendor verification failed for vendor {vendor_id}: {reason}",
|
message=f"Shop verification failed for shop {shop_id}: {reason}",
|
||||||
error_code="VENDOR_VERIFICATION_FAILED",
|
error_code="SHOP_VERIFICATION_FAILED",
|
||||||
details=details,
|
details=details,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -189,3 +189,49 @@ class BulkOperationException(BusinessLogicException):
|
|||||||
error_code="BULK_OPERATION_PARTIAL_FAILURE",
|
error_code="BULK_OPERATION_PARTIAL_FAILURE",
|
||||||
details=details,
|
details=details,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmationRequiredException(BusinessLogicException):
|
||||||
|
"""Raised when a destructive operation requires explicit confirmation."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
operation: str,
|
||||||
|
message: Optional[str] = None,
|
||||||
|
confirmation_param: str = "confirm"
|
||||||
|
):
|
||||||
|
if not message:
|
||||||
|
message = f"Operation '{operation}' requires confirmation parameter: {confirmation_param}=true"
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
message=message,
|
||||||
|
error_code="CONFIRMATION_REQUIRED",
|
||||||
|
details={
|
||||||
|
"operation": operation,
|
||||||
|
"confirmation_param": confirmation_param,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VendorVerificationException(BusinessLogicException):
|
||||||
|
"""Raised when vendor verification fails."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
vendor_id: int,
|
||||||
|
reason: str,
|
||||||
|
current_verification_status: Optional[bool] = None,
|
||||||
|
):
|
||||||
|
details = {
|
||||||
|
"vendor_id": vendor_id,
|
||||||
|
"reason": reason,
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_verification_status is not None:
|
||||||
|
details["current_verification_status"] = current_verification_status
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
message=f"Vendor verification failed for vendor {vendor_id}: {reason}",
|
||||||
|
error_code="VENDOR_VERIFICATION_FAILED",
|
||||||
|
details=details,
|
||||||
|
)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This module provides classes and functions for:
|
|||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
class LetzShopException(Exception):
|
class WizamartException(Exception):
|
||||||
"""Base exception class for all custom exceptions."""
|
"""Base exception class for all custom exceptions."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -41,7 +41,7 @@ class LetzShopException(Exception):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ValidationException(LetzShopException):
|
class ValidationException(WizamartException):
|
||||||
"""Raised when request validation fails."""
|
"""Raised when request validation fails."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -64,7 +64,7 @@ class ValidationException(LetzShopException):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationException(LetzShopException):
|
class AuthenticationException(WizamartException):
|
||||||
"""Raised when authentication fails."""
|
"""Raised when authentication fails."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -81,7 +81,7 @@ class AuthenticationException(LetzShopException):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationException(LetzShopException):
|
class AuthorizationException(WizamartException):
|
||||||
"""Raised when user lacks permission for an operation."""
|
"""Raised when user lacks permission for an operation."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -97,7 +97,7 @@ class AuthorizationException(LetzShopException):
|
|||||||
details=details,
|
details=details,
|
||||||
)
|
)
|
||||||
|
|
||||||
class ResourceNotFoundException(LetzShopException):
|
class ResourceNotFoundException(WizamartException):
|
||||||
"""Raised when a requested resource is not found."""
|
"""Raised when a requested resource is not found."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -122,7 +122,7 @@ class ResourceNotFoundException(LetzShopException):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
class ConflictException(LetzShopException):
|
class ConflictException(WizamartException):
|
||||||
"""Raised when a resource conflict occurs."""
|
"""Raised when a resource conflict occurs."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -138,7 +138,7 @@ class ConflictException(LetzShopException):
|
|||||||
details=details,
|
details=details,
|
||||||
)
|
)
|
||||||
|
|
||||||
class BusinessLogicException(LetzShopException):
|
class BusinessLogicException(WizamartException):
|
||||||
"""Raised when business logic rules are violated."""
|
"""Raised when business logic rules are violated."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -155,7 +155,7 @@ class BusinessLogicException(LetzShopException):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ExternalServiceException(LetzShopException):
|
class ExternalServiceException(WizamartException):
|
||||||
"""Raised when an external service fails."""
|
"""Raised when an external service fails."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -176,7 +176,7 @@ class ExternalServiceException(LetzShopException):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RateLimitException(LetzShopException):
|
class RateLimitException(WizamartException):
|
||||||
"""Raised when rate limit is exceeded."""
|
"""Raised when rate limit is exceeded."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -196,7 +196,7 @@ class RateLimitException(LetzShopException):
|
|||||||
details=rate_limit_details,
|
details=rate_limit_details,
|
||||||
)
|
)
|
||||||
|
|
||||||
class ServiceUnavailableException(LetzShopException):
|
class ServiceUnavailableException(WizamartException):
|
||||||
"""Raised when service is unavailable."""
|
"""Raised when service is unavailable."""
|
||||||
|
|
||||||
def __init__(self, message: str = "Service temporarily unavailable"):
|
def __init__(self, message: str = "Service temporarily unavailable"):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from fastapi import Request, HTTPException
|
|||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.responses import JSONResponse, RedirectResponse
|
from fastapi.responses import JSONResponse, RedirectResponse
|
||||||
|
|
||||||
from .base import LetzShopException
|
from .base import WizamartException
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ logger = logging.getLogger(__name__)
|
|||||||
def setup_exception_handlers(app):
|
def setup_exception_handlers(app):
|
||||||
"""Setup exception handlers for the FastAPI app."""
|
"""Setup exception handlers for the FastAPI app."""
|
||||||
|
|
||||||
@app.exception_handler(LetzShopException)
|
@app.exception_handler(WizamartException)
|
||||||
async def custom_exception_handler(request: Request, exc: LetzShopException):
|
async def custom_exception_handler(request: Request, exc: WizamartException):
|
||||||
"""Handle custom exceptions."""
|
"""Handle custom exceptions."""
|
||||||
|
|
||||||
# Special handling for 401 on HTML page requests (redirect to login)
|
# Special handling for 401 on HTML page requests (redirect to login)
|
||||||
|
|||||||
@@ -535,7 +535,7 @@ class DNSVerificationException(ExternalServiceException):
|
|||||||
| 409 | `ConflictException` | Resource conflicts |
|
| 409 | `ConflictException` | Resource conflicts |
|
||||||
| 422 | `ValidationException` | Input validation errors |
|
| 422 | `ValidationException` | Input validation errors |
|
||||||
| 429 | `RateLimitException` | Rate limiting |
|
| 429 | `RateLimitException` | Rate limiting |
|
||||||
| 500 | `LetzShopException` | Generic errors |
|
| 500 | `WizamartException` | Generic errors |
|
||||||
| 502 | `ExternalServiceException` | Third-party failures |
|
| 502 | `ExternalServiceException` | Third-party failures |
|
||||||
|
|
||||||
### Step 2: Update Exception Exports
|
### Step 2: Update Exception Exports
|
||||||
|
|||||||
@@ -237,7 +237,7 @@
|
|||||||
│ Exception Handler │
|
│ Exception Handler │
|
||||||
│ app/exceptions/handler.py │
|
│ app/exceptions/handler.py │
|
||||||
│ │
|
│ │
|
||||||
│ @app.exception_handler(LetzShopException) │
|
│ @app.exception_handler(WizamartException) │
|
||||||
│ async def custom_exception_handler(...): │
|
│ async def custom_exception_handler(...): │
|
||||||
│ return JSONResponse( │
|
│ return JSONResponse( │
|
||||||
│ status_code=exc.status_code, │
|
│ status_code=exc.status_code, │
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ The LetzShop API uses a unified custom exception system to provide consistent, m
|
|||||||
### Exception Hierarchy
|
### Exception Hierarchy
|
||||||
|
|
||||||
```
|
```
|
||||||
LetzShopException (Base)
|
WizamartException (Base)
|
||||||
├── ValidationException (422)
|
├── ValidationException (422)
|
||||||
├── AuthenticationException (401)
|
├── AuthenticationException (401)
|
||||||
├── AuthorizationException (403)
|
├── AuthorizationException (403)
|
||||||
@@ -91,7 +91,7 @@ The system handles four categories of exceptions:
|
|||||||
```python
|
```python
|
||||||
# app/exceptions/handler.py
|
# app/exceptions/handler.py
|
||||||
|
|
||||||
@app.exception_handler(LetzShopException)
|
@app.exception_handler(WizamartException)
|
||||||
async def custom_exception_handler(request, exc):
|
async def custom_exception_handler(request, exc):
|
||||||
"""Handle all custom business exceptions"""
|
"""Handle all custom business exceptions"""
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ function adminVendorTheme() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const response = await apiClient.get(`/api/v1/admin/vendors/${this.vendorCode}`);
|
const response = await apiClient.get(`/admin/vendors/${this.vendorCode}`);
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
|
|
||||||
this.vendor = response;
|
this.vendor = response;
|
||||||
@@ -139,7 +139,7 @@ function adminVendorTheme() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const response = await apiClient.get(`/api/v1/admin/vendor-themes/${this.vendorCode}`);
|
const response = await apiClient.get(`/admin/vendor-themes/${this.vendorCode}`);
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
@@ -184,7 +184,7 @@ function adminVendorTheme() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const response = await apiClient.get('/api/v1/admin/vendor-themes/presets');
|
const response = await apiClient.get('/admin/vendor-themes/presets');
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
|
|
||||||
this.presets = response.presets || [];
|
this.presets = response.presets || [];
|
||||||
@@ -207,7 +207,7 @@ function adminVendorTheme() {
|
|||||||
try {
|
try {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const response = await apiClient.post(
|
const response = await apiClient.post(
|
||||||
`/api/v1/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`
|
`/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`
|
||||||
);
|
);
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ function adminVendorTheme() {
|
|||||||
try {
|
try {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const response = await apiClient.put(
|
const response = await apiClient.put(
|
||||||
`/api/v1/admin/vendor-themes/${this.vendorCode}`,
|
`/admin/vendor-themes/${this.vendorCode}`,
|
||||||
this.themeData
|
this.themeData
|
||||||
);
|
);
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
|
|||||||
@@ -434,14 +434,14 @@ class TestInventoryAPI:
|
|||||||
assert data["error_code"] == "VALIDATION_ERROR"
|
assert data["error_code"] == "VALIDATION_ERROR"
|
||||||
|
|
||||||
def test_exception_structure_consistency(self, client, auth_headers):
|
def test_exception_structure_consistency(self, client, auth_headers):
|
||||||
"""Test that all inventory exceptions follow the consistent LetzShopException structure"""
|
"""Test that all inventory exceptions follow the consistent WizamartException structure"""
|
||||||
# Test with a known error case
|
# Test with a known error case
|
||||||
response = client.get("/api/v1/inventory/9999999999999", headers=auth_headers)
|
response = client.get("/api/v1/inventory/9999999999999", headers=auth_headers)
|
||||||
|
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
# Verify exception structure matches LetzShopException.to_dict()
|
# Verify exception structure matches WizamartException.to_dict()
|
||||||
required_fields = ["error_code", "message", "status_code"]
|
required_fields = ["error_code", "message", "status_code"]
|
||||||
for field in required_fields:
|
for field in required_fields:
|
||||||
assert field in data, f"Missing required field: {field}"
|
assert field in data, f"Missing required field: {field}"
|
||||||
|
|||||||
@@ -329,14 +329,14 @@ class TestMarketplaceProductsAPI:
|
|||||||
assert data["status_code"] == 401
|
assert data["status_code"] == 401
|
||||||
|
|
||||||
def test_exception_structure_consistency(self, client, auth_headers):
|
def test_exception_structure_consistency(self, client, auth_headers):
|
||||||
"""Test that all exceptions follow the consistent LetzShopException structure"""
|
"""Test that all exceptions follow the consistent WizamartException structure"""
|
||||||
# Test with a known error case
|
# Test with a known error case
|
||||||
response = client.get("/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers)
|
response = client.get("/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers)
|
||||||
|
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
# Verify exception structure matches LetzShopException.to_dict()
|
# Verify exception structure matches WizamartException.to_dict()
|
||||||
required_fields = ["error_code", "message", "status_code"]
|
required_fields = ["error_code", "message", "status_code"]
|
||||||
for field in required_fields:
|
for field in required_fields:
|
||||||
assert field in data, f"Missing required field: {field}"
|
assert field in data, f"Missing required field: {field}"
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ class TestPagination:
|
|||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
# Verify exception structure matches LetzShopException.to_dict()
|
# Verify exception structure matches WizamartException.to_dict()
|
||||||
required_fields = ["error_code", "message", "status_code"]
|
required_fields = ["error_code", "message", "status_code"]
|
||||||
for field in required_fields:
|
for field in required_fields:
|
||||||
assert field in data, f"Missing required field: {field}"
|
assert field in data, f"Missing required field: {field}"
|
||||||
|
|||||||
@@ -368,14 +368,14 @@ class TestVendorsAPI:
|
|||||||
assert data["error_code"] == "VALIDATION_ERROR"
|
assert data["error_code"] == "VALIDATION_ERROR"
|
||||||
|
|
||||||
def test_exception_structure_consistency(self, client, auth_headers):
|
def test_exception_structure_consistency(self, client, auth_headers):
|
||||||
"""Test that all vendor exceptions follow the consistent LetzShopException structure"""
|
"""Test that all vendor exceptions follow the consistent WizamartException structure"""
|
||||||
# Test with a known error case
|
# Test with a known error case
|
||||||
response = client.get("/api/v1/vendor/NONEXISTENT", headers=auth_headers)
|
response = client.get("/api/v1/vendor/NONEXISTENT", headers=auth_headers)
|
||||||
|
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
# Verify exception structure matches LetzShopException.to_dict()
|
# Verify exception structure matches WizamartException.to_dict()
|
||||||
required_fields = ["error_code", "message", "status_code"]
|
required_fields = ["error_code", "message", "status_code"]
|
||||||
for field in required_fields:
|
for field in required_fields:
|
||||||
assert field in data, f"Missing required field: {field}"
|
assert field in data, f"Missing required field: {field}"
|
||||||
|
|||||||
@@ -469,14 +469,14 @@ class TestInventoryService:
|
|||||||
self.service.add_inventory(db, inventory_data_add)
|
self.service.add_inventory(db, inventory_data_add)
|
||||||
|
|
||||||
def test_exception_structure_consistency(self, db):
|
def test_exception_structure_consistency(self, db):
|
||||||
"""Test that all exceptions follow the consistent LetzShopException structure."""
|
"""Test that all exceptions follow the consistent WizamartException structure."""
|
||||||
# Test with a known error case
|
# Test with a known error case
|
||||||
with pytest.raises(InventoryNotFoundException) as exc_info:
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
||||||
self.service.get_inventory_by_gtin(db, "9999999999999")
|
self.service.get_inventory_by_gtin(db, "9999999999999")
|
||||||
|
|
||||||
exception = exc_info.value
|
exception = exc_info.value
|
||||||
|
|
||||||
# Verify exception structure matches LetzShopException.to_dict()
|
# Verify exception structure matches WizamartException.to_dict()
|
||||||
assert hasattr(exception, 'error_code')
|
assert hasattr(exception, 'error_code')
|
||||||
assert hasattr(exception, 'message')
|
assert hasattr(exception, 'message')
|
||||||
assert hasattr(exception, 'status_code')
|
assert hasattr(exception, 'status_code')
|
||||||
|
|||||||
Reference in New Issue
Block a user