Files
orion/app/modules/customers/schemas/context.py
Samir Boulahtit 4b8e1b1d88 refactor(arch): use CustomerContext schema for dependency injection
Phase 5 of storefront restructure plan - fix direct model imports in
API routes by using schemas for dependency injection.

Created CustomerContext schema:
- Lightweight Pydantic model for customer data in API routes
- Populated from Customer DB model in auth dependency
- Contains all fields needed by storefront routes
- Includes from_db_model() factory method

Updated app/api/deps.py:
- _validate_customer_token now returns CustomerContext instead of Customer
- Updated docstrings for all customer auth functions

Updated module storefront routes:
- customers: Uses CustomerContext for profile/address endpoints
- orders: Uses CustomerContext for order history endpoints
- checkout: Uses CustomerContext for order placement
- messaging: Uses CustomerContext for messaging endpoints

This enforces the layered architecture (Routes → Services → Models)
by ensuring API routes never import database models directly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 23:06:21 +01:00

100 lines
2.9 KiB
Python

# app/modules/customers/schemas/context.py
"""
Customer context schema for dependency injection in storefront routes.
This schema provides a clean interface for customer data in API routes,
avoiding direct database model imports in the API layer.
"""
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, ConfigDict
class CustomerContext(BaseModel):
"""
Customer context for dependency injection in storefront routes.
This is a lightweight schema that contains the customer information
needed by API routes. It's populated from the Customer database model
in the authentication dependency.
Usage:
@router.get("/profile")
def get_profile(
customer: CustomerContext = Depends(get_current_customer_api),
):
return {"email": customer.email}
"""
model_config = ConfigDict(from_attributes=True)
# Core identification
id: int
vendor_id: int
email: str
customer_number: str
# Profile info
first_name: str | None = None
last_name: str | None = None
phone: str | None = None
# Preferences
marketing_consent: bool = False
preferred_language: str | None = None
# Stats (for order placement)
total_orders: int = 0
total_spent: Decimal = Decimal("0.00")
last_order_date: datetime | None = None
# Status
is_active: bool = True
# Timestamps
created_at: datetime | None = None
updated_at: datetime | None = None
# Password hash (needed for password change endpoint)
# This is included but should not be exposed in API responses
hashed_password: str | None = None
@property
def full_name(self) -> str:
"""Get customer full name."""
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
return self.email
@classmethod
def from_db_model(cls, customer) -> "CustomerContext":
"""
Create CustomerContext from a Customer database model.
Args:
customer: Customer database model instance
Returns:
CustomerContext: Pydantic schema instance
"""
return cls(
id=customer.id,
vendor_id=customer.vendor_id,
email=customer.email,
customer_number=customer.customer_number,
first_name=customer.first_name,
last_name=customer.last_name,
phone=customer.phone,
marketing_consent=customer.marketing_consent,
preferred_language=customer.preferred_language,
total_orders=customer.total_orders or 0,
total_spent=customer.total_spent or Decimal("0.00"),
last_order_date=customer.last_order_date,
is_active=customer.is_active,
created_at=customer.created_at,
updated_at=customer.updated_at,
hashed_password=customer.hashed_password,
)