refactor: enforce strict architecture rules and add Pydantic response models

- Update architecture rules to be stricter (API-003 now blocks ALL exception
  raising in endpoints, not just HTTPException)
- Update get_current_vendor_api dependency to guarantee token_vendor_id presence
- Remove redundant _get_vendor_from_token helpers from all vendor API files
- Move vendor access validation to service layer methods
- Add Pydantic response models for media, notification, and payment endpoints
- Add get_active_vendor_by_code service method for public vendor lookup
- Add get_import_job_for_vendor service method with vendor validation
- Update validation script to detect exception raising patterns in endpoints

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-04 23:26:03 +01:00
parent cbfbbb4654
commit 81bfc49f77
25 changed files with 1225 additions and 530 deletions

View File

@@ -75,17 +75,42 @@ api_endpoint_rules:
# NOTE: db.commit() is intentionally NOT listed - it's allowed for transaction control
- id: "API-003"
name: "Endpoint must NOT raise HTTPException directly"
name: "Endpoint must NOT raise ANY exceptions directly"
severity: "error"
description: |
API endpoints should NOT raise HTTPException directly. Instead, let domain
exceptions bubble up to the global exception handler which converts them
to appropriate HTTP responses. This ensures consistent error formatting
and centralized error handling.
API endpoints should NOT raise exceptions directly. Endpoints are a thin
orchestration layer that:
1. Accepts request parameters (validated by Pydantic)
2. Calls dependencies for authentication/authorization (deps.py raises exceptions)
3. Calls services for business logic (services raise domain exceptions)
4. Returns response (formatted by Pydantic)
Exception raising belongs in:
- Dependencies (app/api/deps.py) - authentication/authorization validation
- Services (app/services/) - business logic validation
The global exception handler catches all WizamartException subclasses and
converts them to appropriate HTTP responses.
WRONG (endpoint raises exception):
@router.get("/orders")
def get_orders(current_user: User = Depends(get_current_vendor_api)):
if not hasattr(current_user, "token_vendor_id"): # ❌ Redundant check
raise InvalidTokenException("...") # ❌ Don't raise here
return order_service.get_orders(db, current_user.token_vendor_id)
RIGHT (dependency guarantees, endpoint trusts):
@router.get("/orders")
def get_orders(current_user: User = Depends(get_current_vendor_api)):
# Dependency guarantees token_vendor_id is present
return order_service.get_orders(db, current_user.token_vendor_id)
pattern:
file_pattern: "app/api/v1/**/*.py"
anti_patterns:
- "raise HTTPException"
- "raise InvalidTokenException"
- "raise InsufficientPermissionsException"
- "if not hasattr\\(current_user.*token_vendor"
exceptions:
- "app/exceptions/handler.py" # Handler is allowed to use HTTPException