Files
orion/app/modules/customers/exceptions.py
Samir Boulahtit 1b8a40f1ff
All checks were successful
CI / dependency-scanning (push) Successful in 27s
CI / docs (push) Successful in 35s
CI / ruff (push) Successful in 8s
CI / pytest (push) Successful in 34m22s
CI / validate (push) Successful in 19s
CI / deploy (push) Successful in 2m25s
feat(validators): add noqa suppression support to security and performance validators
- Add centralized _is_noqa_suppressed() to BaseValidator with normalization
  (accepts both SEC001 and SEC-001 formats for ruff compatibility)
- Wire noqa support into all 21 security and 18 performance check functions
- Add ruff external config for SEC/PERF/MOD/EXC codes in pyproject.toml
- Convert all 280 Python noqa comments to dashless format (ruff-compatible)
- Add site/ to IGNORE_PATTERNS (excludes mkdocs build output)
- Suppress 152 false positive findings (test passwords, seed data, validator
  self-references, Apple Wallet SHA1, etc.)
- Security: 79 errors → 0, 60 warnings → 0
- Performance: 80 warnings → 77 (3 test script suppressions)
- Add proposal doc with noqa inventory and remaining findings recommendations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:56:56 +01:00

182 lines
5.8 KiB
Python

# app/modules/customers/exceptions.py
"""
Customers module exceptions.
This module provides exception classes for customer operations including:
- Customer management (create, update, authentication)
- Address management
- Password reset
"""
from typing import Any
from app.exceptions.base import (
AuthenticationException,
BusinessLogicException,
ConflictException,
ResourceNotFoundException,
ValidationException,
)
__all__ = [
# Customer exceptions
"CustomerNotFoundException",
"CustomerAlreadyExistsException",
"DuplicateCustomerEmailException",
"CustomerNotActiveException",
"InvalidCustomerCredentialsException",
"CustomerValidationException",
"CustomerAuthorizationException",
"InvalidPasswordResetTokenException",
"PasswordTooShortException",
# Address exceptions
"AddressNotFoundException",
"AddressLimitExceededException",
"InvalidAddressTypeException",
]
# =============================================================================
# Customer Exceptions
# =============================================================================
class CustomerNotFoundException(ResourceNotFoundException):
"""Raised when a customer is not found."""
def __init__(self, customer_identifier: str):
super().__init__(
resource_type="Customer",
identifier=customer_identifier,
message=f"Customer '{customer_identifier}' not found",
error_code="CUSTOMER_NOT_FOUND",
)
class CustomerAlreadyExistsException(ConflictException): # noqa: MOD025
"""Raised when trying to create a customer that already exists."""
def __init__(self, email: str):
super().__init__(
message=f"Customer with email '{email}' already exists",
error_code="CUSTOMER_ALREADY_EXISTS",
details={"email": email},
)
class DuplicateCustomerEmailException(ConflictException):
"""Raised when email already exists for store."""
def __init__(self, email: str, store_code: str):
super().__init__(
message=f"Email '{email}' is already registered for this store",
error_code="DUPLICATE_CUSTOMER_EMAIL",
details={"email": email, "store_code": store_code},
)
class CustomerNotActiveException(BusinessLogicException):
"""Raised when trying to perform operations on inactive customer."""
def __init__(self, email: str):
super().__init__(
message=f"Customer account '{email}' is not active",
error_code="CUSTOMER_NOT_ACTIVE",
details={"email": email},
)
class InvalidCustomerCredentialsException(AuthenticationException):
"""Raised when customer credentials are invalid."""
def __init__(self):
super().__init__(
message="Invalid email or password",
error_code="INVALID_CUSTOMER_CREDENTIALS",
)
class CustomerValidationException(ValidationException):
"""Raised when customer data validation fails."""
def __init__(
self,
message: str = "Customer validation failed",
field: str | None = None,
details: dict[str, Any] | None = None,
):
super().__init__(message=message, field=field, details=details)
self.error_code = "CUSTOMER_VALIDATION_FAILED"
class CustomerAuthorizationException(BusinessLogicException): # noqa: MOD025
"""Raised when customer is not authorized for operation."""
def __init__(self, customer_email: str, operation: str):
super().__init__(
message=f"Customer '{customer_email}' not authorized for: {operation}",
error_code="CUSTOMER_NOT_AUTHORIZED",
details={"customer_email": customer_email, "operation": operation},
)
class InvalidPasswordResetTokenException(ValidationException):
"""Raised when password reset token is invalid or expired."""
def __init__(self):
super().__init__(
message="Invalid or expired password reset link. Please request a new one.",
field="reset_token",
)
self.error_code = "INVALID_RESET_TOKEN"
class PasswordTooShortException(ValidationException):
"""Raised when password doesn't meet minimum length requirement."""
def __init__(self, min_length: int = 8):
super().__init__(
message=f"Password must be at least {min_length} characters long",
field="password",
details={"min_length": min_length},
)
self.error_code = "PASSWORD_TOO_SHORT"
# =============================================================================
# Address Exceptions
# =============================================================================
class AddressNotFoundException(ResourceNotFoundException):
"""Raised when a customer address is not found."""
def __init__(self, address_id: str | int):
super().__init__(
resource_type="Address",
identifier=str(address_id),
error_code="ADDRESS_NOT_FOUND",
)
class AddressLimitExceededException(BusinessLogicException):
"""Raised when customer exceeds maximum number of addresses."""
def __init__(self, max_addresses: int = 10):
super().__init__(
message=f"Maximum number of addresses ({max_addresses}) reached",
error_code="ADDRESS_LIMIT_EXCEEDED",
details={"max_addresses": max_addresses},
)
class InvalidAddressTypeException(BusinessLogicException): # noqa: MOD025
"""Raised when an invalid address type is provided."""
def __init__(self, address_type: str):
super().__init__(
message=f"Invalid address type '{address_type}'. Must be 'shipping' or 'billing'",
error_code="INVALID_ADDRESS_TYPE",
details={"address_type": address_type},
)