YO / validators.py
NEXAS's picture
Upload 12 files
b3cb0b5 verified
"""
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')
# Remove all non-digits
digits = re.sub(r'\D', '', v)
# Check length (7-15 digits is standard international range)
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:
# Parse ISO 8601 datetime
dt = datetime.fromisoformat(v.replace('Z', '+00:00'))
# Check if in the future
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):
# Remove potentially dangerous characters
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):
# Allow alphanumeric, hyphens, and underscores only
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
# Helper functions for easy validation
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