style: apply black and isort formatting across entire codebase
- Standardize quote style (single to double quotes) - Reorder and group imports alphabetically - Fix line breaks and indentation for consistency - Apply PEP 8 formatting standards Also updated Makefile to exclude both venv and .venv from code quality checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,179 +6,109 @@ This module provides frontend-friendly exceptions with consistent error codes,
|
||||
messages, and HTTP status mappings.
|
||||
"""
|
||||
|
||||
# Base exceptions
|
||||
from .base import (
|
||||
WizamartException,
|
||||
ValidationException,
|
||||
AuthenticationException,
|
||||
AuthorizationException,
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
BusinessLogicException,
|
||||
ExternalServiceException,
|
||||
RateLimitException,
|
||||
ServiceUnavailableException,
|
||||
)
|
||||
|
||||
# Authentication exceptions
|
||||
from .auth import (
|
||||
InvalidCredentialsException,
|
||||
TokenExpiredException,
|
||||
InvalidTokenException,
|
||||
InsufficientPermissionsException,
|
||||
UserNotActiveException,
|
||||
AdminRequiredException,
|
||||
UserAlreadyExistsException
|
||||
)
|
||||
|
||||
# Admin exceptions
|
||||
from .admin import (
|
||||
UserNotFoundException,
|
||||
UserStatusChangeException,
|
||||
VendorVerificationException,
|
||||
AdminOperationException,
|
||||
CannotModifyAdminException,
|
||||
CannotModifySelfException,
|
||||
InvalidAdminActionException,
|
||||
BulkOperationException,
|
||||
ConfirmationRequiredException,
|
||||
)
|
||||
|
||||
# Marketplace import job exceptions
|
||||
from .marketplace_import_job import (
|
||||
MarketplaceImportException,
|
||||
ImportJobNotFoundException,
|
||||
ImportJobNotOwnedException,
|
||||
InvalidImportDataException,
|
||||
ImportJobCannotBeCancelledException,
|
||||
ImportJobCannotBeDeletedException,
|
||||
MarketplaceConnectionException,
|
||||
MarketplaceDataParsingException,
|
||||
ImportRateLimitException,
|
||||
InvalidMarketplaceException,
|
||||
ImportJobAlreadyProcessingException,
|
||||
)
|
||||
|
||||
# Marketplace product exceptions
|
||||
from .marketplace_product import (
|
||||
MarketplaceProductNotFoundException,
|
||||
MarketplaceProductAlreadyExistsException,
|
||||
InvalidMarketplaceProductDataException,
|
||||
MarketplaceProductValidationException,
|
||||
InvalidGTINException,
|
||||
MarketplaceProductCSVImportException,
|
||||
)
|
||||
|
||||
# Inventory exceptions
|
||||
from .inventory import (
|
||||
InventoryNotFoundException,
|
||||
InsufficientInventoryException,
|
||||
InvalidInventoryOperationException,
|
||||
InventoryValidationException,
|
||||
NegativeInventoryException,
|
||||
InvalidQuantityException,
|
||||
LocationNotFoundException
|
||||
)
|
||||
|
||||
# Vendor exceptions
|
||||
from .vendor import (
|
||||
VendorNotFoundException,
|
||||
VendorAlreadyExistsException,
|
||||
VendorNotActiveException,
|
||||
VendorNotVerifiedException,
|
||||
UnauthorizedVendorAccessException,
|
||||
InvalidVendorDataException,
|
||||
MaxVendorsReachedException,
|
||||
VendorValidationException,
|
||||
)
|
||||
|
||||
# Vendor domain exceptions
|
||||
from .vendor_domain import (
|
||||
VendorDomainNotFoundException,
|
||||
VendorDomainAlreadyExistsException,
|
||||
InvalidDomainFormatException,
|
||||
ReservedDomainException,
|
||||
DomainNotVerifiedException,
|
||||
DomainVerificationFailedException,
|
||||
DomainAlreadyVerifiedException,
|
||||
MultiplePrimaryDomainsException,
|
||||
DNSVerificationException,
|
||||
MaxDomainsReachedException,
|
||||
UnauthorizedDomainAccessException,
|
||||
)
|
||||
|
||||
# Vendor theme exceptions
|
||||
from .vendor_theme import (
|
||||
VendorThemeNotFoundException,
|
||||
InvalidThemeDataException,
|
||||
ThemePresetNotFoundException,
|
||||
ThemeValidationException,
|
||||
ThemePresetAlreadyAppliedException,
|
||||
InvalidColorFormatException,
|
||||
InvalidFontFamilyException,
|
||||
ThemeOperationException,
|
||||
)
|
||||
|
||||
# Customer exceptions
|
||||
from .customer import (
|
||||
CustomerNotFoundException,
|
||||
CustomerAlreadyExistsException,
|
||||
DuplicateCustomerEmailException,
|
||||
CustomerNotActiveException,
|
||||
InvalidCustomerCredentialsException,
|
||||
CustomerValidationException,
|
||||
CustomerAuthorizationException,
|
||||
)
|
||||
|
||||
# Team exceptions
|
||||
from .team import (
|
||||
TeamMemberNotFoundException,
|
||||
TeamMemberAlreadyExistsException,
|
||||
TeamInvitationNotFoundException,
|
||||
TeamInvitationExpiredException,
|
||||
TeamInvitationAlreadyAcceptedException,
|
||||
UnauthorizedTeamActionException,
|
||||
CannotRemoveOwnerException,
|
||||
CannotModifyOwnRoleException,
|
||||
RoleNotFoundException,
|
||||
InvalidRoleException,
|
||||
InsufficientTeamPermissionsException,
|
||||
MaxTeamMembersReachedException,
|
||||
TeamValidationException,
|
||||
InvalidInvitationDataException,
|
||||
InvalidInvitationTokenException,
|
||||
)
|
||||
|
||||
# Product exceptions
|
||||
from .product import (
|
||||
ProductNotFoundException,
|
||||
ProductAlreadyExistsException,
|
||||
ProductNotInCatalogException,
|
||||
ProductNotActiveException,
|
||||
InvalidProductDataException,
|
||||
ProductValidationException,
|
||||
CannotDeleteProductWithInventoryException,
|
||||
CannotDeleteProductWithOrdersException,
|
||||
)
|
||||
|
||||
# Order exceptions
|
||||
from .order import (
|
||||
OrderNotFoundException,
|
||||
OrderAlreadyExistsException,
|
||||
OrderValidationException,
|
||||
InvalidOrderStatusException,
|
||||
OrderCannotBeCancelledException,
|
||||
)
|
||||
|
||||
from .admin import (AdminOperationException, BulkOperationException,
|
||||
CannotModifyAdminException, CannotModifySelfException,
|
||||
ConfirmationRequiredException, InvalidAdminActionException,
|
||||
UserNotFoundException, UserStatusChangeException,
|
||||
VendorVerificationException)
|
||||
# Authentication exceptions
|
||||
from .auth import (AdminRequiredException, InsufficientPermissionsException,
|
||||
InvalidCredentialsException, InvalidTokenException,
|
||||
TokenExpiredException, UserAlreadyExistsException,
|
||||
UserNotActiveException)
|
||||
# Base exceptions
|
||||
from .base import (AuthenticationException, AuthorizationException,
|
||||
BusinessLogicException, ConflictException,
|
||||
ExternalServiceException, RateLimitException,
|
||||
ResourceNotFoundException, ServiceUnavailableException,
|
||||
ValidationException, WizamartException)
|
||||
# Cart exceptions
|
||||
from .cart import (
|
||||
CartItemNotFoundException,
|
||||
EmptyCartException,
|
||||
CartValidationException,
|
||||
InsufficientInventoryForCartException,
|
||||
InvalidCartQuantityException,
|
||||
ProductNotAvailableForCartException,
|
||||
)
|
||||
from .cart import (CartItemNotFoundException, CartValidationException,
|
||||
EmptyCartException, InsufficientInventoryForCartException,
|
||||
InvalidCartQuantityException,
|
||||
ProductNotAvailableForCartException)
|
||||
# Customer exceptions
|
||||
from .customer import (CustomerAlreadyExistsException,
|
||||
CustomerAuthorizationException,
|
||||
CustomerNotActiveException, CustomerNotFoundException,
|
||||
CustomerValidationException,
|
||||
DuplicateCustomerEmailException,
|
||||
InvalidCustomerCredentialsException)
|
||||
# Inventory exceptions
|
||||
from .inventory import (InsufficientInventoryException,
|
||||
InvalidInventoryOperationException,
|
||||
InvalidQuantityException, InventoryNotFoundException,
|
||||
InventoryValidationException,
|
||||
LocationNotFoundException, NegativeInventoryException)
|
||||
# Marketplace import job exceptions
|
||||
from .marketplace_import_job import (ImportJobAlreadyProcessingException,
|
||||
ImportJobCannotBeCancelledException,
|
||||
ImportJobCannotBeDeletedException,
|
||||
ImportJobNotFoundException,
|
||||
ImportJobNotOwnedException,
|
||||
ImportRateLimitException,
|
||||
InvalidImportDataException,
|
||||
InvalidMarketplaceException,
|
||||
MarketplaceConnectionException,
|
||||
MarketplaceDataParsingException,
|
||||
MarketplaceImportException)
|
||||
# Marketplace product exceptions
|
||||
from .marketplace_product import (InvalidGTINException,
|
||||
InvalidMarketplaceProductDataException,
|
||||
MarketplaceProductAlreadyExistsException,
|
||||
MarketplaceProductCSVImportException,
|
||||
MarketplaceProductNotFoundException,
|
||||
MarketplaceProductValidationException)
|
||||
# Order exceptions
|
||||
from .order import (InvalidOrderStatusException, OrderAlreadyExistsException,
|
||||
OrderCannotBeCancelledException, OrderNotFoundException,
|
||||
OrderValidationException)
|
||||
# Product exceptions
|
||||
from .product import (CannotDeleteProductWithInventoryException,
|
||||
CannotDeleteProductWithOrdersException,
|
||||
InvalidProductDataException,
|
||||
ProductAlreadyExistsException, ProductNotActiveException,
|
||||
ProductNotFoundException, ProductNotInCatalogException,
|
||||
ProductValidationException)
|
||||
# Team exceptions
|
||||
from .team import (CannotModifyOwnRoleException, CannotRemoveOwnerException,
|
||||
InsufficientTeamPermissionsException,
|
||||
InvalidInvitationDataException,
|
||||
InvalidInvitationTokenException, InvalidRoleException,
|
||||
MaxTeamMembersReachedException, RoleNotFoundException,
|
||||
TeamInvitationAlreadyAcceptedException,
|
||||
TeamInvitationExpiredException,
|
||||
TeamInvitationNotFoundException,
|
||||
TeamMemberAlreadyExistsException,
|
||||
TeamMemberNotFoundException, TeamValidationException,
|
||||
UnauthorizedTeamActionException)
|
||||
# Vendor exceptions
|
||||
from .vendor import (InvalidVendorDataException, MaxVendorsReachedException,
|
||||
UnauthorizedVendorAccessException,
|
||||
VendorAlreadyExistsException, VendorNotActiveException,
|
||||
VendorNotFoundException, VendorNotVerifiedException,
|
||||
VendorValidationException)
|
||||
# Vendor domain exceptions
|
||||
from .vendor_domain import (DNSVerificationException,
|
||||
DomainAlreadyVerifiedException,
|
||||
DomainNotVerifiedException,
|
||||
DomainVerificationFailedException,
|
||||
InvalidDomainFormatException,
|
||||
MaxDomainsReachedException,
|
||||
MultiplePrimaryDomainsException,
|
||||
ReservedDomainException,
|
||||
UnauthorizedDomainAccessException,
|
||||
VendorDomainAlreadyExistsException,
|
||||
VendorDomainNotFoundException)
|
||||
# Vendor theme exceptions
|
||||
from .vendor_theme import (InvalidColorFormatException,
|
||||
InvalidFontFamilyException,
|
||||
InvalidThemeDataException, ThemeOperationException,
|
||||
ThemePresetAlreadyAppliedException,
|
||||
ThemePresetNotFoundException,
|
||||
ThemeValidationException,
|
||||
VendorThemeNotFoundException)
|
||||
|
||||
__all__ = [
|
||||
# Base exceptions
|
||||
@@ -192,7 +122,6 @@ __all__ = [
|
||||
"ExternalServiceException",
|
||||
"RateLimitException",
|
||||
"ServiceUnavailableException",
|
||||
|
||||
# Auth exceptions
|
||||
"InvalidCredentialsException",
|
||||
"TokenExpiredException",
|
||||
@@ -201,7 +130,6 @@ __all__ = [
|
||||
"UserNotActiveException",
|
||||
"AdminRequiredException",
|
||||
"UserAlreadyExistsException",
|
||||
|
||||
# Customer exceptions
|
||||
"CustomerNotFoundException",
|
||||
"CustomerAlreadyExistsException",
|
||||
@@ -210,7 +138,6 @@ __all__ = [
|
||||
"InvalidCustomerCredentialsException",
|
||||
"CustomerValidationException",
|
||||
"CustomerAuthorizationException",
|
||||
|
||||
# Team exceptions
|
||||
"TeamMemberNotFoundException",
|
||||
"TeamMemberAlreadyExistsException",
|
||||
@@ -227,7 +154,6 @@ __all__ = [
|
||||
"TeamValidationException",
|
||||
"InvalidInvitationDataException",
|
||||
"InvalidInvitationTokenException",
|
||||
|
||||
# Inventory exceptions
|
||||
"InventoryNotFoundException",
|
||||
"InsufficientInventoryException",
|
||||
@@ -236,7 +162,6 @@ __all__ = [
|
||||
"NegativeInventoryException",
|
||||
"InvalidQuantityException",
|
||||
"LocationNotFoundException",
|
||||
|
||||
# Vendor exceptions
|
||||
"VendorNotFoundException",
|
||||
"VendorAlreadyExistsException",
|
||||
@@ -246,7 +171,6 @@ __all__ = [
|
||||
"InvalidVendorDataException",
|
||||
"MaxVendorsReachedException",
|
||||
"VendorValidationException",
|
||||
|
||||
# Vendor Domain
|
||||
"VendorDomainNotFoundException",
|
||||
"VendorDomainAlreadyExistsException",
|
||||
@@ -259,7 +183,6 @@ __all__ = [
|
||||
"DNSVerificationException",
|
||||
"MaxDomainsReachedException",
|
||||
"UnauthorizedDomainAccessException",
|
||||
|
||||
# Vendor Theme
|
||||
"VendorThemeNotFoundException",
|
||||
"InvalidThemeDataException",
|
||||
@@ -269,7 +192,6 @@ __all__ = [
|
||||
"InvalidColorFormatException",
|
||||
"InvalidFontFamilyException",
|
||||
"ThemeOperationException",
|
||||
|
||||
# Product exceptions
|
||||
"ProductNotFoundException",
|
||||
"ProductAlreadyExistsException",
|
||||
@@ -279,14 +201,12 @@ __all__ = [
|
||||
"ProductValidationException",
|
||||
"CannotDeleteProductWithInventoryException",
|
||||
"CannotDeleteProductWithOrdersException",
|
||||
|
||||
# Order exceptions
|
||||
"OrderNotFoundException",
|
||||
"OrderAlreadyExistsException",
|
||||
"OrderValidationException",
|
||||
"InvalidOrderStatusException",
|
||||
"OrderCannotBeCancelledException",
|
||||
|
||||
# Cart exceptions
|
||||
"CartItemNotFoundException",
|
||||
"EmptyCartException",
|
||||
@@ -294,7 +214,6 @@ __all__ = [
|
||||
"InsufficientInventoryForCartException",
|
||||
"InvalidCartQuantityException",
|
||||
"ProductNotAvailableForCartException",
|
||||
|
||||
# MarketplaceProduct exceptions
|
||||
"MarketplaceProductNotFoundException",
|
||||
"MarketplaceProductAlreadyExistsException",
|
||||
@@ -302,7 +221,6 @@ __all__ = [
|
||||
"MarketplaceProductValidationException",
|
||||
"InvalidGTINException",
|
||||
"MarketplaceProductCSVImportException",
|
||||
|
||||
# Marketplace import exceptions
|
||||
"MarketplaceImportException",
|
||||
"ImportJobNotFoundException",
|
||||
@@ -315,7 +233,6 @@ __all__ = [
|
||||
"ImportRateLimitException",
|
||||
"InvalidMarketplaceException",
|
||||
"ImportJobAlreadyProcessingException",
|
||||
|
||||
# Admin exceptions
|
||||
"UserNotFoundException",
|
||||
"UserStatusChangeException",
|
||||
|
||||
@@ -4,12 +4,9 @@ Admin operations specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
BusinessLogicException,
|
||||
AuthorizationException,
|
||||
ValidationException
|
||||
)
|
||||
|
||||
from .base import (AuthorizationException, BusinessLogicException,
|
||||
ResourceNotFoundException, ValidationException)
|
||||
|
||||
|
||||
class UserNotFoundException(ResourceNotFoundException):
|
||||
@@ -35,11 +32,11 @@ class UserStatusChangeException(BusinessLogicException):
|
||||
"""Raised when user status cannot be changed."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user_id: int,
|
||||
current_status: str,
|
||||
attempted_action: str,
|
||||
reason: Optional[str] = None,
|
||||
self,
|
||||
user_id: int,
|
||||
current_status: str,
|
||||
attempted_action: str,
|
||||
reason: Optional[str] = None,
|
||||
):
|
||||
message = f"Cannot {attempted_action} user {user_id} (current status: {current_status})"
|
||||
if reason:
|
||||
@@ -61,10 +58,10 @@ class ShopVerificationException(BusinessLogicException):
|
||||
"""Raised when shop verification fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
shop_id: int,
|
||||
reason: str,
|
||||
current_verification_status: Optional[bool] = None,
|
||||
self,
|
||||
shop_id: int,
|
||||
reason: str,
|
||||
current_verification_status: Optional[bool] = None,
|
||||
):
|
||||
details = {
|
||||
"shop_id": shop_id,
|
||||
@@ -85,11 +82,11 @@ class AdminOperationException(BusinessLogicException):
|
||||
"""Raised when admin operation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
operation: str,
|
||||
reason: str,
|
||||
target_type: Optional[str] = None,
|
||||
target_id: Optional[str] = None,
|
||||
self,
|
||||
operation: str,
|
||||
reason: str,
|
||||
target_type: Optional[str] = None,
|
||||
target_id: Optional[str] = None,
|
||||
):
|
||||
message = f"Admin operation '{operation}' failed: {reason}"
|
||||
|
||||
@@ -142,10 +139,10 @@ class InvalidAdminActionException(ValidationException):
|
||||
"""Raised when admin action is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
action: str,
|
||||
reason: str,
|
||||
valid_actions: Optional[list] = None,
|
||||
self,
|
||||
action: str,
|
||||
reason: str,
|
||||
valid_actions: Optional[list] = None,
|
||||
):
|
||||
details = {
|
||||
"action": action,
|
||||
@@ -166,11 +163,11 @@ class BulkOperationException(BusinessLogicException):
|
||||
"""Raised when bulk admin operation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
operation: str,
|
||||
total_items: int,
|
||||
failed_items: int,
|
||||
errors: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
operation: str,
|
||||
total_items: int,
|
||||
failed_items: int,
|
||||
errors: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
message = f"Bulk {operation} completed with errors: {failed_items}/{total_items} failed"
|
||||
|
||||
@@ -195,10 +192,10 @@ class ConfirmationRequiredException(BusinessLogicException):
|
||||
"""Raised when a destructive operation requires explicit confirmation."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
operation: str,
|
||||
message: Optional[str] = None,
|
||||
confirmation_param: str = "confirm"
|
||||
self,
|
||||
operation: str,
|
||||
message: Optional[str] = None,
|
||||
confirmation_param: str = "confirm",
|
||||
):
|
||||
if not message:
|
||||
message = f"Operation '{operation}' requires confirmation parameter: {confirmation_param}=true"
|
||||
@@ -217,10 +214,10 @@ class VendorVerificationException(BusinessLogicException):
|
||||
"""Raised when vendor verification fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vendor_id: int,
|
||||
reason: str,
|
||||
current_verification_status: Optional[bool] = None,
|
||||
self,
|
||||
vendor_id: int,
|
||||
reason: str,
|
||||
current_verification_status: Optional[bool] = None,
|
||||
):
|
||||
details = {
|
||||
"vendor_id": vendor_id,
|
||||
|
||||
@@ -4,7 +4,9 @@ Authentication and authorization specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base import AuthenticationException, AuthorizationException, ConflictException
|
||||
|
||||
from .base import (AuthenticationException, AuthorizationException,
|
||||
ConflictException)
|
||||
|
||||
|
||||
class InvalidCredentialsException(AuthenticationException):
|
||||
@@ -41,9 +43,9 @@ class InsufficientPermissionsException(AuthorizationException):
|
||||
"""Raised when user lacks required permissions for an action."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Insufficient permissions for this action",
|
||||
required_permission: Optional[str] = None,
|
||||
self,
|
||||
message: str = "Insufficient permissions for this action",
|
||||
required_permission: Optional[str] = None,
|
||||
):
|
||||
details = {}
|
||||
if required_permission:
|
||||
@@ -80,9 +82,9 @@ class UserAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to register with existing username/email."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "User already exists",
|
||||
field: Optional[str] = None,
|
||||
self,
|
||||
message: str = "User already exists",
|
||||
field: Optional[str] = None,
|
||||
):
|
||||
details = {}
|
||||
if field:
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Backup/recovery exceptions
|
||||
# Backup/recovery exceptions
|
||||
|
||||
@@ -39,8 +39,6 @@ class WizamartException(Exception):
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
class ValidationException(WizamartException):
|
||||
"""Raised when request validation fails."""
|
||||
|
||||
@@ -62,8 +60,6 @@ class ValidationException(WizamartException):
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class AuthenticationException(WizamartException):
|
||||
"""Raised when authentication fails."""
|
||||
|
||||
@@ -97,6 +93,7 @@ class AuthorizationException(WizamartException):
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class ResourceNotFoundException(WizamartException):
|
||||
"""Raised when a requested resource is not found."""
|
||||
|
||||
@@ -122,6 +119,7 @@ class ResourceNotFoundException(WizamartException):
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ConflictException(WizamartException):
|
||||
"""Raised when a resource conflict occurs."""
|
||||
|
||||
@@ -138,6 +136,7 @@ class ConflictException(WizamartException):
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class BusinessLogicException(WizamartException):
|
||||
"""Raised when business logic rules are violated."""
|
||||
|
||||
@@ -196,6 +195,7 @@ class RateLimitException(WizamartException):
|
||||
details=rate_limit_details,
|
||||
)
|
||||
|
||||
|
||||
class ServiceUnavailableException(WizamartException):
|
||||
"""Raised when service is unavailable."""
|
||||
|
||||
@@ -206,6 +206,7 @@ class ServiceUnavailableException(WizamartException):
|
||||
status_code=503,
|
||||
)
|
||||
|
||||
|
||||
# Note: Domain-specific exceptions like VendorNotFoundException, UserNotFoundException, etc.
|
||||
# are defined in their respective domain modules (vendor.py, admin.py, etc.)
|
||||
# to keep domain-specific logic separate from base exceptions.
|
||||
|
||||
@@ -4,11 +4,9 @@ Shopping cart specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ValidationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (BusinessLogicException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class CartItemNotFoundException(ResourceNotFoundException):
|
||||
@@ -19,22 +17,16 @@ class CartItemNotFoundException(ResourceNotFoundException):
|
||||
resource_type="CartItem",
|
||||
identifier=str(product_id),
|
||||
message=f"Product {product_id} not found in cart",
|
||||
error_code="CART_ITEM_NOT_FOUND"
|
||||
error_code="CART_ITEM_NOT_FOUND",
|
||||
)
|
||||
self.details.update({
|
||||
"product_id": product_id,
|
||||
"session_id": session_id
|
||||
})
|
||||
self.details.update({"product_id": product_id, "session_id": session_id})
|
||||
|
||||
|
||||
class EmptyCartException(ValidationException):
|
||||
"""Raised when trying to perform operations on an empty cart."""
|
||||
|
||||
def __init__(self, session_id: str):
|
||||
super().__init__(
|
||||
message="Cart is empty",
|
||||
details={"session_id": session_id}
|
||||
)
|
||||
super().__init__(message="Cart is empty", details={"session_id": session_id})
|
||||
self.error_code = "CART_EMPTY"
|
||||
|
||||
|
||||
@@ -82,7 +74,9 @@ class InsufficientInventoryForCartException(BusinessLogicException):
|
||||
class InvalidCartQuantityException(ValidationException):
|
||||
"""Raised when cart quantity is invalid."""
|
||||
|
||||
def __init__(self, quantity: int, min_quantity: int = 1, max_quantity: Optional[int] = None):
|
||||
def __init__(
|
||||
self, quantity: int, min_quantity: int = 1, max_quantity: Optional[int] = None
|
||||
):
|
||||
if quantity < min_quantity:
|
||||
message = f"Quantity must be at least {min_quantity}"
|
||||
elif max_quantity and quantity > max_quantity:
|
||||
|
||||
@@ -4,13 +4,10 @@ Customer management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
AuthenticationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (AuthenticationException, BusinessLogicException,
|
||||
ConflictException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class CustomerNotFoundException(ResourceNotFoundException):
|
||||
@@ -21,7 +18,7 @@ class CustomerNotFoundException(ResourceNotFoundException):
|
||||
resource_type="Customer",
|
||||
identifier=customer_identifier,
|
||||
message=f"Customer '{customer_identifier}' not found",
|
||||
error_code="CUSTOMER_NOT_FOUND"
|
||||
error_code="CUSTOMER_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +29,7 @@ class CustomerAlreadyExistsException(ConflictException):
|
||||
super().__init__(
|
||||
message=f"Customer with email '{email}' already exists",
|
||||
error_code="CUSTOMER_ALREADY_EXISTS",
|
||||
details={"email": email}
|
||||
details={"email": email},
|
||||
)
|
||||
|
||||
|
||||
@@ -43,10 +40,7 @@ class DuplicateCustomerEmailException(ConflictException):
|
||||
super().__init__(
|
||||
message=f"Email '{email}' is already registered for this vendor",
|
||||
error_code="DUPLICATE_CUSTOMER_EMAIL",
|
||||
details={
|
||||
"email": email,
|
||||
"vendor_code": vendor_code
|
||||
}
|
||||
details={"email": email, "vendor_code": vendor_code},
|
||||
)
|
||||
|
||||
|
||||
@@ -57,7 +51,7 @@ class CustomerNotActiveException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Customer account '{email}' is not active",
|
||||
error_code="CUSTOMER_NOT_ACTIVE",
|
||||
details={"email": email}
|
||||
details={"email": email},
|
||||
)
|
||||
|
||||
|
||||
@@ -67,7 +61,7 @@ class InvalidCustomerCredentialsException(AuthenticationException):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
message="Invalid email or password",
|
||||
error_code="INVALID_CUSTOMER_CREDENTIALS"
|
||||
error_code="INVALID_CUSTOMER_CREDENTIALS",
|
||||
)
|
||||
|
||||
|
||||
@@ -78,13 +72,9 @@ class CustomerValidationException(ValidationException):
|
||||
self,
|
||||
message: str = "Customer validation failed",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
field=field,
|
||||
details=details
|
||||
)
|
||||
super().__init__(message=message, field=field, details=details)
|
||||
self.error_code = "CUSTOMER_VALIDATION_FAILED"
|
||||
|
||||
|
||||
@@ -95,8 +85,5 @@ class CustomerAuthorizationException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Customer '{customer_email}' not authorized for: {operation}",
|
||||
error_code="CUSTOMER_NOT_AUTHORIZED",
|
||||
details={
|
||||
"customer_email": customer_email,
|
||||
"operation": operation
|
||||
}
|
||||
details={"customer_email": customer_email, "operation": operation},
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ Handles fallback logic and context-specific customization.
|
||||
"""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
@@ -114,7 +114,7 @@ class ErrorPageRenderer:
|
||||
"error_code": error_code,
|
||||
"context": context_type.value,
|
||||
"template": template_path,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -129,8 +129,7 @@ class ErrorPageRenderer:
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to render error template {template_path}: {e}",
|
||||
exc_info=True
|
||||
f"Failed to render error template {template_path}: {e}", exc_info=True
|
||||
)
|
||||
# Return basic HTML as absolute fallback
|
||||
return ErrorPageRenderer._render_basic_html_fallback(
|
||||
@@ -228,7 +227,9 @@ class ErrorPageRenderer:
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _get_context_data(request: Request, context_type: RequestContext) -> Dict[str, Any]:
|
||||
def _get_context_data(
|
||||
request: Request, context_type: RequestContext
|
||||
) -> Dict[str, Any]:
|
||||
"""Get context-specific data for error templates."""
|
||||
data = {}
|
||||
|
||||
@@ -261,11 +262,19 @@ class ErrorPageRenderer:
|
||||
|
||||
# Calculate base_url for shop links
|
||||
vendor_context = getattr(request.state, "vendor_context", None)
|
||||
access_method = vendor_context.get('detection_method', 'unknown') if vendor_context else 'unknown'
|
||||
access_method = (
|
||||
vendor_context.get("detection_method", "unknown")
|
||||
if vendor_context
|
||||
else "unknown"
|
||||
)
|
||||
base_url = "/"
|
||||
if access_method == "path" and vendor:
|
||||
# Use the full_prefix from vendor_context to determine which pattern was used
|
||||
full_prefix = vendor_context.get('full_prefix', '/vendor/') if vendor_context else '/vendor/'
|
||||
full_prefix = (
|
||||
vendor_context.get("full_prefix", "/vendor/")
|
||||
if vendor_context
|
||||
else "/vendor/"
|
||||
)
|
||||
base_url = f"{full_prefix}{vendor.subdomain}/"
|
||||
data["base_url"] = base_url
|
||||
|
||||
|
||||
@@ -13,13 +13,14 @@ This module provides classes and functions for:
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi import HTTPException, Request
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import JSONResponse, RedirectResponse
|
||||
|
||||
from middleware.context import RequestContext, get_request_context
|
||||
|
||||
from .base import WizamartException
|
||||
from .error_renderer import ErrorPageRenderer
|
||||
from middleware.context import RequestContext, get_request_context
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -38,8 +39,8 @@ def setup_exception_handlers(app):
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"accept": request.headers.get("accept", ""),
|
||||
"method": request.method
|
||||
}
|
||||
"method": request.method,
|
||||
},
|
||||
)
|
||||
|
||||
# Redirect to appropriate login page based on context
|
||||
@@ -56,15 +57,12 @@ def setup_exception_handlers(app):
|
||||
"url": str(request.url),
|
||||
"method": request.method,
|
||||
"exception_type": type(exc).__name__,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an API request
|
||||
if _is_api_request(request):
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=exc.to_dict()
|
||||
)
|
||||
return JSONResponse(status_code=exc.status_code, content=exc.to_dict())
|
||||
|
||||
# Check if this is an HTML page request
|
||||
if _is_html_page_request(request):
|
||||
@@ -78,10 +76,7 @@ def setup_exception_handlers(app):
|
||||
)
|
||||
|
||||
# Default to JSON for unknown request types
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=exc.to_dict()
|
||||
)
|
||||
return JSONResponse(status_code=exc.status_code, content=exc.to_dict())
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(request: Request, exc: HTTPException):
|
||||
@@ -96,7 +91,7 @@ def setup_exception_handlers(app):
|
||||
"url": str(request.url),
|
||||
"method": request.method,
|
||||
"exception_type": "HTTPException",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an API request
|
||||
@@ -107,7 +102,7 @@ def setup_exception_handlers(app):
|
||||
"error_code": f"HTTP_{exc.status_code}",
|
||||
"message": exc.detail,
|
||||
"status_code": exc.status_code,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an HTML page request
|
||||
@@ -128,11 +123,13 @@ def setup_exception_handlers(app):
|
||||
"error_code": f"HTTP_{exc.status_code}",
|
||||
"message": exc.detail,
|
||||
"status_code": exc.status_code,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
async def validation_exception_handler(
|
||||
request: Request, exc: RequestValidationError
|
||||
):
|
||||
"""Handle Pydantic validation errors with consistent format."""
|
||||
|
||||
# Sanitize errors to remove sensitive data from logs
|
||||
@@ -140,8 +137,8 @@ def setup_exception_handlers(app):
|
||||
for error in exc.errors():
|
||||
sanitized_error = error.copy()
|
||||
# Remove 'input' field which may contain passwords
|
||||
if 'input' in sanitized_error:
|
||||
sanitized_error['input'] = '<redacted>'
|
||||
if "input" in sanitized_error:
|
||||
sanitized_error["input"] = "<redacted>"
|
||||
sanitized_errors.append(sanitized_error)
|
||||
|
||||
logger.error(
|
||||
@@ -151,7 +148,7 @@ def setup_exception_handlers(app):
|
||||
"url": str(request.url),
|
||||
"method": request.method,
|
||||
"exception_type": "RequestValidationError",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Clean up validation errors to ensure JSON serializability
|
||||
@@ -159,15 +156,17 @@ def setup_exception_handlers(app):
|
||||
for error in exc.errors():
|
||||
clean_error = {}
|
||||
for key, value in error.items():
|
||||
if key == 'input' and isinstance(value, bytes):
|
||||
if key == "input" and isinstance(value, bytes):
|
||||
# Convert bytes to string representation for JSON serialization
|
||||
clean_error[key] = f"<bytes: {len(value)} bytes>"
|
||||
elif key == 'ctx' and isinstance(value, dict):
|
||||
elif key == "ctx" and isinstance(value, dict):
|
||||
# Handle the 'ctx' field that contains ValueError objects
|
||||
clean_ctx = {}
|
||||
for ctx_key, ctx_value in value.items():
|
||||
if isinstance(ctx_value, Exception):
|
||||
clean_ctx[ctx_key] = str(ctx_value) # Convert exception to string
|
||||
clean_ctx[ctx_key] = str(
|
||||
ctx_value
|
||||
) # Convert exception to string
|
||||
else:
|
||||
clean_ctx[ctx_key] = ctx_value
|
||||
clean_error[key] = clean_ctx
|
||||
@@ -186,10 +185,8 @@ def setup_exception_handlers(app):
|
||||
"error_code": "VALIDATION_ERROR",
|
||||
"message": "Request validation failed",
|
||||
"status_code": 422,
|
||||
"details": {
|
||||
"validation_errors": clean_errors
|
||||
}
|
||||
}
|
||||
"details": {"validation_errors": clean_errors},
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an HTML page request
|
||||
@@ -210,10 +207,8 @@ def setup_exception_handlers(app):
|
||||
"error_code": "VALIDATION_ERROR",
|
||||
"message": "Request validation failed",
|
||||
"status_code": 422,
|
||||
"details": {
|
||||
"validation_errors": clean_errors
|
||||
}
|
||||
}
|
||||
"details": {"validation_errors": clean_errors},
|
||||
},
|
||||
)
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
@@ -227,7 +222,7 @@ def setup_exception_handlers(app):
|
||||
"url": str(request.url),
|
||||
"method": request.method,
|
||||
"exception_type": type(exc).__name__,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an API request
|
||||
@@ -238,7 +233,7 @@ def setup_exception_handlers(app):
|
||||
"error_code": "INTERNAL_SERVER_ERROR",
|
||||
"message": "Internal server error",
|
||||
"status_code": 500,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an HTML page request
|
||||
@@ -259,7 +254,7 @@ def setup_exception_handlers(app):
|
||||
"error_code": "INTERNAL_SERVER_ERROR",
|
||||
"message": "Internal server error",
|
||||
"status_code": 500,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@app.exception_handler(404)
|
||||
@@ -275,11 +270,8 @@ def setup_exception_handlers(app):
|
||||
"error_code": "ENDPOINT_NOT_FOUND",
|
||||
"message": f"Endpoint not found: {request.url.path}",
|
||||
"status_code": 404,
|
||||
"details": {
|
||||
"path": request.url.path,
|
||||
"method": request.method
|
||||
}
|
||||
}
|
||||
"details": {"path": request.url.path, "method": request.method},
|
||||
},
|
||||
)
|
||||
|
||||
# Check if this is an HTML page request
|
||||
@@ -300,11 +292,8 @@ def setup_exception_handlers(app):
|
||||
"error_code": "ENDPOINT_NOT_FOUND",
|
||||
"message": f"Endpoint not found: {request.url.path}",
|
||||
"status_code": 404,
|
||||
"details": {
|
||||
"path": request.url.path,
|
||||
"method": request.method
|
||||
}
|
||||
}
|
||||
"details": {"path": request.url.path, "method": request.method},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -332,8 +321,8 @@ def _is_html_page_request(request: Request) -> bool:
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"method": request.method,
|
||||
"accept": request.headers.get("accept", "")
|
||||
}
|
||||
"accept": request.headers.get("accept", ""),
|
||||
},
|
||||
)
|
||||
|
||||
# Don't redirect API calls
|
||||
@@ -354,7 +343,9 @@ def _is_html_page_request(request: Request) -> bool:
|
||||
# MUST explicitly accept HTML (strict check)
|
||||
accept_header = request.headers.get("accept", "")
|
||||
if "text/html" not in accept_header:
|
||||
logger.debug(f"Not HTML page: Accept header doesn't include text/html: {accept_header}")
|
||||
logger.debug(
|
||||
f"Not HTML page: Accept header doesn't include text/html: {accept_header}"
|
||||
)
|
||||
return False
|
||||
|
||||
logger.debug("IS HTML page request")
|
||||
@@ -379,13 +370,21 @@ def _redirect_to_login(request: Request) -> RedirectResponse:
|
||||
elif context_type == RequestContext.SHOP:
|
||||
# For shop context, redirect to shop login (customer login)
|
||||
# Calculate base_url for proper routing (supports domain, subdomain, and path-based access)
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor_context = getattr(request.state, 'vendor_context', None)
|
||||
access_method = vendor_context.get('detection_method', 'unknown') if vendor_context else 'unknown'
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
vendor_context = getattr(request.state, "vendor_context", None)
|
||||
access_method = (
|
||||
vendor_context.get("detection_method", "unknown")
|
||||
if vendor_context
|
||||
else "unknown"
|
||||
)
|
||||
|
||||
base_url = "/"
|
||||
if access_method == "path" and vendor:
|
||||
full_prefix = vendor_context.get('full_prefix', '/vendor/') if vendor_context else '/vendor/'
|
||||
full_prefix = (
|
||||
vendor_context.get("full_prefix", "/vendor/")
|
||||
if vendor_context
|
||||
else "/vendor/"
|
||||
)
|
||||
base_url = f"{full_prefix}{vendor.subdomain}/"
|
||||
|
||||
login_url = f"{base_url}shop/account/login"
|
||||
@@ -401,22 +400,28 @@ def _redirect_to_login(request: Request) -> RedirectResponse:
|
||||
def raise_not_found(resource_type: str, identifier: str) -> None:
|
||||
"""Convenience function to raise ResourceNotFoundException."""
|
||||
from .base import ResourceNotFoundException
|
||||
|
||||
raise ResourceNotFoundException(resource_type, identifier)
|
||||
|
||||
|
||||
def raise_validation_error(message: str, field: str = None, details: dict = None) -> None:
|
||||
def raise_validation_error(
|
||||
message: str, field: str = None, details: dict = None
|
||||
) -> None:
|
||||
"""Convenience function to raise ValidationException."""
|
||||
from .base import ValidationException
|
||||
|
||||
raise ValidationException(message, field, details)
|
||||
|
||||
|
||||
def raise_auth_error(message: str = "Authentication failed") -> None:
|
||||
"""Convenience function to raise AuthenticationException."""
|
||||
from .base import AuthenticationException
|
||||
|
||||
raise AuthenticationException(message)
|
||||
|
||||
|
||||
def raise_permission_error(message: str = "Access denied") -> None:
|
||||
"""Convenience function to raise AuthorizationException."""
|
||||
from .base import AuthorizationException
|
||||
|
||||
raise AuthorizationException(message)
|
||||
|
||||
@@ -4,7 +4,9 @@ Inventory management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import ResourceNotFoundException, ValidationException, BusinessLogicException
|
||||
|
||||
from .base import (BusinessLogicException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class InventoryNotFoundException(ResourceNotFoundException):
|
||||
@@ -14,7 +16,9 @@ class InventoryNotFoundException(ResourceNotFoundException):
|
||||
if identifier_type.lower() == "gtin":
|
||||
message = f"No inventory found for GTIN '{identifier}'"
|
||||
else:
|
||||
message = f"Inventory record with {identifier_type} '{identifier}' not found"
|
||||
message = (
|
||||
f"Inventory record with {identifier_type} '{identifier}' not found"
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
resource_type="Inventory",
|
||||
@@ -28,11 +32,11 @@ class InsufficientInventoryException(BusinessLogicException):
|
||||
"""Raised when trying to remove more inventory than available."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
gtin: str,
|
||||
location: str,
|
||||
requested: int,
|
||||
available: int,
|
||||
self,
|
||||
gtin: str,
|
||||
location: str,
|
||||
requested: int,
|
||||
available: int,
|
||||
):
|
||||
message = f"Insufficient inventory for GTIN '{gtin}' at '{location}'. Requested: {requested}, Available: {available}"
|
||||
|
||||
@@ -52,10 +56,10 @@ class InvalidInventoryOperationException(ValidationException):
|
||||
"""Raised when inventory operation is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
operation: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str,
|
||||
operation: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
if not details:
|
||||
details = {}
|
||||
@@ -74,10 +78,10 @@ class InventoryValidationException(ValidationException):
|
||||
"""Raised when inventory data validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Inventory validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
self,
|
||||
message: str = "Inventory validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
details = {}
|
||||
if validation_errors:
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Import/marketplace exceptions
|
||||
# Import/marketplace exceptions
|
||||
|
||||
@@ -4,24 +4,21 @@ Marketplace import specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ValidationException,
|
||||
BusinessLogicException,
|
||||
AuthorizationException,
|
||||
ExternalServiceException
|
||||
)
|
||||
|
||||
from .base import (AuthorizationException, BusinessLogicException,
|
||||
ExternalServiceException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class MarketplaceImportException(BusinessLogicException):
|
||||
"""Base exception for marketplace import operations."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
error_code: str = "MARKETPLACE_IMPORT_ERROR",
|
||||
marketplace: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str,
|
||||
error_code: str = "MARKETPLACE_IMPORT_ERROR",
|
||||
marketplace: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
if not details:
|
||||
details = {}
|
||||
@@ -67,11 +64,11 @@ class InvalidImportDataException(ValidationException):
|
||||
"""Raised when import data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid import data",
|
||||
field: Optional[str] = None,
|
||||
row_number: Optional[int] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "Invalid import data",
|
||||
field: Optional[str] = None,
|
||||
row_number: Optional[int] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
if not details:
|
||||
details = {}
|
||||
@@ -118,7 +115,9 @@ class ImportJobCannotBeDeletedException(BusinessLogicException):
|
||||
class MarketplaceConnectionException(ExternalServiceException):
|
||||
"""Raised when marketplace connection fails."""
|
||||
|
||||
def __init__(self, marketplace: str, message: str = "Failed to connect to marketplace"):
|
||||
def __init__(
|
||||
self, marketplace: str, message: str = "Failed to connect to marketplace"
|
||||
):
|
||||
super().__init__(
|
||||
service=marketplace,
|
||||
message=f"{message}: {marketplace}",
|
||||
@@ -130,10 +129,10 @@ class MarketplaceDataParsingException(ValidationException):
|
||||
"""Raised when marketplace data cannot be parsed."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
marketplace: str,
|
||||
message: str = "Failed to parse marketplace data",
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
marketplace: str,
|
||||
message: str = "Failed to parse marketplace data",
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
if not details:
|
||||
details = {}
|
||||
@@ -150,10 +149,10 @@ class ImportRateLimitException(BusinessLogicException):
|
||||
"""Raised when import rate limit is exceeded."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
max_imports: int,
|
||||
time_window: str,
|
||||
retry_after: Optional[int] = None,
|
||||
self,
|
||||
max_imports: int,
|
||||
time_window: str,
|
||||
retry_after: Optional[int] = None,
|
||||
):
|
||||
details = {
|
||||
"max_imports": max_imports,
|
||||
|
||||
@@ -4,7 +4,9 @@ MarketplaceProduct management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import ResourceNotFoundException, ConflictException, ValidationException, BusinessLogicException
|
||||
|
||||
from .base import (BusinessLogicException, ConflictException,
|
||||
ResourceNotFoundException, ValidationException)
|
||||
|
||||
|
||||
class MarketplaceProductNotFoundException(ResourceNotFoundException):
|
||||
@@ -34,10 +36,10 @@ class InvalidMarketplaceProductDataException(ValidationException):
|
||||
"""Raised when product data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid product data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "Invalid product data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
@@ -51,10 +53,10 @@ class MarketplaceProductValidationException(ValidationException):
|
||||
"""Raised when product validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
self,
|
||||
message: str,
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
details = {}
|
||||
if validation_errors:
|
||||
@@ -84,10 +86,10 @@ class MarketplaceProductCSVImportException(BusinessLogicException):
|
||||
"""Raised when product CSV import fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "MarketplaceProduct CSV import failed",
|
||||
row_number: Optional[int] = None,
|
||||
errors: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "MarketplaceProduct CSV import failed",
|
||||
row_number: Optional[int] = None,
|
||||
errors: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
details = {}
|
||||
if row_number:
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Media/file management exceptions
|
||||
# Media/file management exceptions
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Monitoring exceptions
|
||||
# Monitoring exceptions
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Notification exceptions
|
||||
# Notification exceptions
|
||||
|
||||
@@ -4,11 +4,9 @@ Order management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ValidationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (BusinessLogicException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class OrderNotFoundException(ResourceNotFoundException):
|
||||
@@ -19,7 +17,7 @@ class OrderNotFoundException(ResourceNotFoundException):
|
||||
resource_type="Order",
|
||||
identifier=order_identifier,
|
||||
message=f"Order '{order_identifier}' not found",
|
||||
error_code="ORDER_NOT_FOUND"
|
||||
error_code="ORDER_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
@@ -30,7 +28,7 @@ class OrderAlreadyExistsException(ValidationException):
|
||||
super().__init__(
|
||||
message=f"Order with number '{order_number}' already exists",
|
||||
error_code="ORDER_ALREADY_EXISTS",
|
||||
details={"order_number": order_number}
|
||||
details={"order_number": order_number},
|
||||
)
|
||||
|
||||
|
||||
@@ -39,9 +37,7 @@ class OrderValidationException(ValidationException):
|
||||
|
||||
def __init__(self, message: str, details: Optional[dict] = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code="ORDER_VALIDATION_FAILED",
|
||||
details=details
|
||||
message=message, error_code="ORDER_VALIDATION_FAILED", details=details
|
||||
)
|
||||
|
||||
|
||||
@@ -52,10 +48,7 @@ class InvalidOrderStatusException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Cannot change order status from '{current_status}' to '{new_status}'",
|
||||
error_code="INVALID_ORDER_STATUS_CHANGE",
|
||||
details={
|
||||
"current_status": current_status,
|
||||
"new_status": new_status
|
||||
}
|
||||
details={"current_status": current_status, "new_status": new_status},
|
||||
)
|
||||
|
||||
|
||||
@@ -66,8 +59,5 @@ class OrderCannotBeCancelledException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Order '{order_number}' cannot be cancelled: {reason}",
|
||||
error_code="ORDER_CANNOT_BE_CANCELLED",
|
||||
details={
|
||||
"order_number": order_number,
|
||||
"reason": reason
|
||||
}
|
||||
details={"order_number": order_number, "reason": reason},
|
||||
)
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Payment processing exceptions
|
||||
# Payment processing exceptions
|
||||
|
||||
@@ -4,12 +4,9 @@ Product (vendor catalog) specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (BusinessLogicException, ConflictException,
|
||||
ResourceNotFoundException, ValidationException)
|
||||
|
||||
|
||||
class ProductNotFoundException(ResourceNotFoundException):
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Search exceptions
|
||||
# Search exceptions
|
||||
|
||||
@@ -4,13 +4,10 @@ Team management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
AuthorizationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (AuthorizationException, BusinessLogicException,
|
||||
ConflictException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class TeamMemberNotFoundException(ResourceNotFoundException):
|
||||
@@ -20,7 +17,9 @@ class TeamMemberNotFoundException(ResourceNotFoundException):
|
||||
details = {"user_id": user_id}
|
||||
if vendor_id:
|
||||
details["vendor_id"] = vendor_id
|
||||
message = f"Team member with user ID '{user_id}' not found in vendor {vendor_id}"
|
||||
message = (
|
||||
f"Team member with user ID '{user_id}' not found in vendor {vendor_id}"
|
||||
)
|
||||
else:
|
||||
message = f"Team member with user ID '{user_id}' not found"
|
||||
|
||||
@@ -84,7 +83,12 @@ class TeamInvitationAlreadyAcceptedException(ConflictException):
|
||||
class UnauthorizedTeamActionException(AuthorizationException):
|
||||
"""Raised when user tries to perform team action without permission."""
|
||||
|
||||
def __init__(self, action: str, user_id: Optional[int] = None, required_permission: Optional[str] = None):
|
||||
def __init__(
|
||||
self,
|
||||
action: str,
|
||||
user_id: Optional[int] = None,
|
||||
required_permission: Optional[str] = None,
|
||||
):
|
||||
details = {"action": action}
|
||||
if user_id:
|
||||
details["user_id"] = user_id
|
||||
@@ -147,10 +151,10 @@ class InvalidRoleException(ValidationException):
|
||||
"""Raised when role data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid role data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "Invalid role data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
@@ -164,10 +168,10 @@ class InsufficientTeamPermissionsException(AuthorizationException):
|
||||
"""Raised when user lacks required team permissions for an action."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
required_permission: str,
|
||||
user_id: Optional[int] = None,
|
||||
action: Optional[str] = None,
|
||||
self,
|
||||
required_permission: str,
|
||||
user_id: Optional[int] = None,
|
||||
action: Optional[str] = None,
|
||||
):
|
||||
details = {"required_permission": required_permission}
|
||||
if user_id:
|
||||
@@ -202,10 +206,10 @@ class TeamValidationException(ValidationException):
|
||||
"""Raised when team operation validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Team operation validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
self,
|
||||
message: str = "Team operation validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
details = {}
|
||||
if validation_errors:
|
||||
@@ -223,10 +227,10 @@ class InvalidInvitationDataException(ValidationException):
|
||||
"""Raised when team invitation data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid invitation data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "Invalid invitation data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
@@ -240,6 +244,7 @@ class InvalidInvitationDataException(ValidationException):
|
||||
# NEW: Add InvalidInvitationTokenException
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class InvalidInvitationTokenException(ValidationException):
|
||||
"""Raised when invitation token is invalid, expired, or already used.
|
||||
|
||||
@@ -248,9 +253,9 @@ class InvalidInvitationTokenException(ValidationException):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid or expired invitation token",
|
||||
invitation_token: Optional[str] = None
|
||||
self,
|
||||
message: str = "Invalid or expired invitation token",
|
||||
invitation_token: Optional[str] = None,
|
||||
):
|
||||
details = {}
|
||||
if invitation_token:
|
||||
|
||||
@@ -4,13 +4,10 @@ Vendor management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
AuthorizationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (AuthorizationException, BusinessLogicException,
|
||||
ConflictException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class VendorNotFoundException(ResourceNotFoundException):
|
||||
@@ -82,10 +79,10 @@ class InvalidVendorDataException(ValidationException):
|
||||
"""Raised when vendor data is invalid or incomplete."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid vendor data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "Invalid vendor data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
@@ -99,10 +96,10 @@ class VendorValidationException(ValidationException):
|
||||
"""Raised when vendor validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Vendor validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
self,
|
||||
message: str = "Vendor validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
details = {}
|
||||
if validation_errors:
|
||||
@@ -120,9 +117,9 @@ class IncompleteVendorDataException(ValidationException):
|
||||
"""Raised when vendor data is missing required fields."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vendor_code: str,
|
||||
missing_fields: list,
|
||||
self,
|
||||
vendor_code: str,
|
||||
missing_fields: list,
|
||||
):
|
||||
super().__init__(
|
||||
message=f"Vendor '{vendor_code}' is missing required fields: {', '.join(missing_fields)}",
|
||||
|
||||
@@ -4,13 +4,10 @@ Vendor domain management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
BusinessLogicException,
|
||||
ExternalServiceException
|
||||
)
|
||||
|
||||
from .base import (BusinessLogicException, ConflictException,
|
||||
ExternalServiceException, ResourceNotFoundException,
|
||||
ValidationException)
|
||||
|
||||
|
||||
class VendorDomainNotFoundException(ResourceNotFoundException):
|
||||
@@ -64,10 +61,7 @@ class ReservedDomainException(ValidationException):
|
||||
super().__init__(
|
||||
message=f"Domain cannot use reserved subdomain: {reserved_part}",
|
||||
field="domain",
|
||||
details={
|
||||
"domain": domain,
|
||||
"reserved_part": reserved_part
|
||||
},
|
||||
details={"domain": domain, "reserved_part": reserved_part},
|
||||
)
|
||||
self.error_code = "RESERVED_DOMAIN"
|
||||
|
||||
@@ -79,10 +73,7 @@ class DomainNotVerifiedException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Domain '{domain}' must be verified before activation",
|
||||
error_code="DOMAIN_NOT_VERIFIED",
|
||||
details={
|
||||
"domain_id": domain_id,
|
||||
"domain": domain
|
||||
},
|
||||
details={"domain_id": domain_id, "domain": domain},
|
||||
)
|
||||
|
||||
|
||||
@@ -93,10 +84,7 @@ class DomainVerificationFailedException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Domain verification failed for '{domain}': {reason}",
|
||||
error_code="DOMAIN_VERIFICATION_FAILED",
|
||||
details={
|
||||
"domain": domain,
|
||||
"reason": reason
|
||||
},
|
||||
details={"domain": domain, "reason": reason},
|
||||
)
|
||||
|
||||
|
||||
@@ -107,10 +95,7 @@ class DomainAlreadyVerifiedException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Domain '{domain}' is already verified",
|
||||
error_code="DOMAIN_ALREADY_VERIFIED",
|
||||
details={
|
||||
"domain_id": domain_id,
|
||||
"domain": domain
|
||||
},
|
||||
details={"domain_id": domain_id, "domain": domain},
|
||||
)
|
||||
|
||||
|
||||
@@ -133,10 +118,7 @@ class DNSVerificationException(ExternalServiceException):
|
||||
service_name="DNS",
|
||||
message=f"DNS verification failed for '{domain}': {reason}",
|
||||
error_code="DNS_VERIFICATION_ERROR",
|
||||
details={
|
||||
"domain": domain,
|
||||
"reason": reason
|
||||
},
|
||||
details={"domain": domain, "reason": reason},
|
||||
)
|
||||
|
||||
|
||||
@@ -147,10 +129,7 @@ class MaxDomainsReachedException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Maximum number of domains reached ({max_domains})",
|
||||
error_code="MAX_DOMAINS_REACHED",
|
||||
details={
|
||||
"vendor_id": vendor_id,
|
||||
"max_domains": max_domains
|
||||
},
|
||||
details={"vendor_id": vendor_id, "max_domains": max_domains},
|
||||
)
|
||||
|
||||
|
||||
@@ -161,8 +140,5 @@ class UnauthorizedDomainAccessException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Unauthorized access to domain {domain_id}",
|
||||
error_code="UNAUTHORIZED_DOMAIN_ACCESS",
|
||||
details={
|
||||
"domain_id": domain_id,
|
||||
"vendor_id": vendor_id
|
||||
},
|
||||
details={"domain_id": domain_id, "vendor_id": vendor_id},
|
||||
)
|
||||
|
||||
@@ -4,12 +4,9 @@ Vendor theme management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
from .base import (BusinessLogicException, ConflictException,
|
||||
ResourceNotFoundException, ValidationException)
|
||||
|
||||
|
||||
class VendorThemeNotFoundException(ResourceNotFoundException):
|
||||
@@ -28,10 +25,10 @@ class InvalidThemeDataException(ValidationException):
|
||||
"""Raised when theme data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid theme data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
self,
|
||||
message: str = "Invalid theme data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
@@ -62,10 +59,10 @@ class ThemeValidationException(ValidationException):
|
||||
"""Raised when theme validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Theme validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
self,
|
||||
message: str = "Theme validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
details = {}
|
||||
if validation_errors:
|
||||
@@ -86,10 +83,7 @@ class ThemePresetAlreadyAppliedException(BusinessLogicException):
|
||||
super().__init__(
|
||||
message=f"Preset '{preset_name}' is already applied to vendor '{vendor_code}'",
|
||||
error_code="THEME_PRESET_ALREADY_APPLIED",
|
||||
details={
|
||||
"preset_name": preset_name,
|
||||
"vendor_code": vendor_code
|
||||
},
|
||||
details={"preset_name": preset_name, "vendor_code": vendor_code},
|
||||
)
|
||||
|
||||
|
||||
@@ -120,18 +114,13 @@ class InvalidFontFamilyException(ValidationException):
|
||||
class ThemeOperationException(BusinessLogicException):
|
||||
"""Raised when theme operation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
operation: str,
|
||||
vendor_code: str,
|
||||
reason: str
|
||||
):
|
||||
def __init__(self, operation: str, vendor_code: str, reason: str):
|
||||
super().__init__(
|
||||
message=f"Theme operation '{operation}' failed for vendor '{vendor_code}': {reason}",
|
||||
error_code="THEME_OPERATION_FAILED",
|
||||
details={
|
||||
"operation": operation,
|
||||
"vendor_code": vendor_code,
|
||||
"reason": reason
|
||||
"reason": reason,
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user