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>
This commit is contained in:
2026-01-29 23:06:21 +01:00
parent 2755c2f780
commit 4b8e1b1d88
8 changed files with 137 additions and 32 deletions

View File

@@ -33,6 +33,7 @@ from app.exceptions import (
ConversationNotFoundException,
VendorNotFoundException,
)
from app.modules.customers.schemas import CustomerContext
from app.modules.messaging.models.message import ConversationType, ParticipantType
from app.modules.messaging.schemas import (
ConversationDetailResponse,
@@ -45,7 +46,6 @@ from app.modules.messaging.services import (
message_attachment_service,
messaging_service,
)
from models.database.customer import Customer
router = APIRouter()
logger = logging.getLogger(__name__)
@@ -74,7 +74,7 @@ def list_conversations(
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=100),
status: Optional[str] = Query(None, pattern="^(open|closed)$"),
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""
@@ -146,7 +146,7 @@ def list_conversations(
@router.get("/messages/unread-count", response_model=UnreadCountResponse)
def get_unread_count(
request: Request,
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""
@@ -171,7 +171,7 @@ def get_unread_count(
def get_conversation(
request: Request,
conversation_id: int = Path(..., description="Conversation ID", gt=0),
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""
@@ -261,7 +261,7 @@ async def send_message(
conversation_id: int = Path(..., description="Conversation ID", gt=0),
content: str = Form(..., min_length=1, max_length=10000),
attachments: List[UploadFile] = File(default=[]),
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""
@@ -359,7 +359,7 @@ async def send_message(
def mark_as_read(
request: Request,
conversation_id: int = Path(..., description="Conversation ID", gt=0),
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""Mark conversation as read."""
@@ -394,7 +394,7 @@ async def download_attachment(
request: Request,
conversation_id: int = Path(..., description="Conversation ID", gt=0),
attachment_id: int = Path(..., description="Attachment ID", gt=0),
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""
@@ -439,7 +439,7 @@ async def get_attachment_thumbnail(
request: Request,
conversation_id: int = Path(..., description="Conversation ID", gt=0),
attachment_id: int = Path(..., description="Attachment ID", gt=0),
customer: Customer = Depends(get_current_customer_api),
customer: CustomerContext = Depends(get_current_customer_api),
db: Session = Depends(get_db),
):
"""
@@ -503,7 +503,7 @@ def _get_other_participant_name(conversation, customer_id: int) -> str:
def _get_sender_name(message) -> str:
"""Get sender name for a message."""
if message.sender_type == ParticipantType.CUSTOMER:
from models.database.customer import Customer
from app.modules.customers.models import Customer
customer = (
Customer.query.filter_by(id=message.sender_id).first()