| """
|
| Input Validators using Pydantic
|
| Ensures data integrity and security
|
| """
|
| from pydantic import BaseModel, validator, Field
|
| from datetime import datetime
|
| import re
|
|
|
| class PhoneNumber(BaseModel):
|
| """Validate phone numbers"""
|
| number: str = Field(..., description="Phone number to validate")
|
|
|
| @validator('number')
|
| def validate_phone(cls, v):
|
| if not v:
|
| raise ValueError('Phone number cannot be empty')
|
|
|
|
|
| digits = re.sub(r'\D', '', v)
|
|
|
|
|
| if len(digits) < 7 or len(digits) > 15:
|
| raise ValueError(f'Invalid phone number length: {len(digits)} digits')
|
|
|
| return digits
|
|
|
| @property
|
| def formatted(self):
|
| """Return formatted phone number"""
|
| return self.number
|
|
|
| class AppointmentTime(BaseModel):
|
| """Validate appointment times"""
|
| time: str = Field(..., description="ISO 8601 datetime string")
|
|
|
| @validator('time')
|
| def validate_time(cls, v):
|
| try:
|
|
|
| dt = datetime.fromisoformat(v.replace('Z', '+00:00'))
|
|
|
|
|
| if dt < datetime.now():
|
| raise ValueError('Appointment time must be in the future')
|
|
|
| return v
|
| except ValueError as e:
|
| raise ValueError(f'Invalid datetime format: {e}')
|
|
|
| class AppointmentPurpose(BaseModel):
|
| """Validate appointment purpose"""
|
| purpose: str = Field(..., min_length=3, max_length=200)
|
|
|
| @validator('purpose')
|
| def validate_purpose(cls, v):
|
|
|
| cleaned = re.sub(r'[<>{}]', '', v)
|
|
|
| if len(cleaned.strip()) < 3:
|
| raise ValueError('Purpose must be at least 3 characters')
|
|
|
| return cleaned.strip()
|
|
|
| class AppointmentId(BaseModel):
|
| """Validate appointment ID"""
|
| id: str = Field(..., description="Appointment ID")
|
|
|
| @validator('id')
|
| def validate_id(cls, v):
|
|
|
| if not re.match(r'^[a-zA-Z0-9_-]+$', v):
|
| raise ValueError('Invalid appointment ID format')
|
|
|
| if len(v) > 100:
|
| raise ValueError('Appointment ID too long')
|
|
|
| return v
|
|
|
|
|
| def validate_phone_number(number: str) -> str:
|
| """Validate and return cleaned phone number"""
|
| validated = PhoneNumber(number=number)
|
| return validated.formatted
|
|
|
| def validate_appointment_time(time: str) -> str:
|
| """Validate appointment time"""
|
| validated = AppointmentTime(time=time)
|
| return validated.time
|
|
|
| def validate_purpose(purpose: str) -> str:
|
| """Validate appointment purpose"""
|
| validated = AppointmentPurpose(purpose=purpose)
|
| return validated.purpose
|
|
|
| def validate_appointment_id(id: str) -> str:
|
| """Validate appointment ID"""
|
| validated = AppointmentId(id=id)
|
| return validated.id
|
|
|