- Replace `except Exception` with specific exception types in google_wallet_service.py (requests.RequestException, ValueError, etc.) and apple_wallet_service.py (httpx.HTTPError, OSError, ssl.SSLError) - Rename loyalty_onboarding.py -> loyalty_onboarding_service.py to match NAM-002 naming convention (+ test file + imports) - Add PasswordChangeResponse Pydantic model to user_account API, removing raw dict return and noqa suppression Resolves 12 EXC-003 + 1 NAM-002 architecture warnings in loyalty module. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
# app/modules/tenancy/schemas/user_account.py
|
|
"""
|
|
Self-service account schemas for logged-in users.
|
|
|
|
Used by admin, store, and merchant frontends to let users
|
|
manage their own identity (name, email, password).
|
|
"""
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, EmailStr, Field, field_validator
|
|
|
|
|
|
class UserAccountResponse(BaseModel):
|
|
"""Self-service account info returned to the logged-in user."""
|
|
|
|
id: int
|
|
email: str
|
|
username: str
|
|
first_name: str | None = None
|
|
last_name: str | None = None
|
|
role: str
|
|
preferred_language: str | None = None
|
|
is_email_verified: bool = False
|
|
last_login: datetime | None = None
|
|
created_at: datetime | None = None
|
|
updated_at: datetime | None = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class UserAccountUpdate(BaseModel):
|
|
"""Fields the user can edit about themselves."""
|
|
|
|
first_name: str | None = Field(None, max_length=100)
|
|
last_name: str | None = Field(None, max_length=100)
|
|
email: EmailStr | None = None
|
|
preferred_language: str | None = Field(None, pattern=r"^(en|fr|de|lb)$")
|
|
|
|
|
|
class UserPasswordChange(BaseModel):
|
|
"""Password change with current-password verification."""
|
|
|
|
current_password: str = Field(..., description="Current password")
|
|
new_password: str = Field(
|
|
..., min_length=8, description="New password (minimum 8 characters)"
|
|
)
|
|
confirm_password: str = Field(..., description="Confirm new password")
|
|
|
|
@field_validator("new_password")
|
|
@classmethod
|
|
def password_strength(cls, v: str) -> str:
|
|
"""Validate password strength."""
|
|
if not any(char.isdigit() for char in v):
|
|
raise ValueError("Password must contain at least one digit")
|
|
if not any(char.isalpha() for char in v):
|
|
raise ValueError("Password must contain at least one letter")
|
|
return v
|
|
|
|
|
|
class PasswordChangeResponse(BaseModel):
|
|
"""Response for a successful password change."""
|
|
|
|
message: str
|