feat: Updated endpoint endpoints/contact.post.py via AI with auto lint fixes

This commit is contained in:
Backend IM Bot 2025-04-15 17:35:05 +00:00
parent 497f9fa71d
commit d52d1d6b75
4 changed files with 95 additions and 36 deletions

View File

@ -0,0 +1,29 @@
"""create contacts table
Revision ID: 8a3d5f2e1c9b
Revises: 0002
Create Date: 2024-01-23 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '8a3d5f2e1c9b'
down_revision = '0002'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
'contacts',
sa.Column('id', sa.String(36), primary_key=True),
sa.Column('name', sa.String(), nullable=False),
sa.Column('email', sa.String(), nullable=False),
sa.Column('message', sa.Text(), nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.func.now()),
sa.Column('updated_at', sa.DateTime(), server_default=sa.func.now())
)
op.create_index(op.f('ix_contacts_email'), 'contacts', ['email'], unique=False)
def downgrade():
op.drop_index(op.f('ix_contacts_email'), table_name='contacts')
op.drop_table('contacts')

View File

@ -1,16 +1,27 @@
from fastapi import APIRouter, Depends, status from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from core.database import get_db from core.database import get_db
from schemas.contact import ContactCreate, ContactSchema from schemas.contact import ContactCreate, ContactSchema
from utils.contact_helpers import sanitize_contact_input, create_contact, format_contact_response from typing import Dict, Any
from utils.contact_utils import sanitize_contact_input, validate_contact_data
from services.contact_service import create_contact, format_contact_response
router = APIRouter() router = APIRouter()
@router.post("/contact", response_model=ContactSchema, status_code=status.HTTP_201_CREATED) @router.post("/contact", status_code=status.HTTP_201_CREATED, response_model=ContactSchema)
async def create_contact_submission( async def create_contact_submission(
contact_data: ContactCreate, contact_data: Dict[str, Any],
db: Session = Depends(get_db) db: Session = Depends(get_db)
): ):
sanitized_data = sanitize_contact_input(contact_data.dict()) sanitized_data = sanitize_contact_input(contact_data)
contact = create_contact(db=db, contact_data=ContactCreate(**sanitized_data)) validation_errors = validate_contact_data(sanitized_data)
return format_contact_response(contact)
if validation_errors:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=validation_errors
)
contact_create = ContactCreate(**sanitized_data)
db_contact = create_contact(db=db, contact_data=contact_create)
return format_contact_response(db_contact)

View File

@ -3,7 +3,7 @@ from sqlalchemy.orm import Session
from models.contact import Contact from models.contact import Contact
from schemas.contact import ContactCreate, ContactSchema from schemas.contact import ContactCreate, ContactSchema
from fastapi import HTTPException, status from fastapi import HTTPException, status
import email_validator from pydantic import EmailStr, ValidationError
def validate_contact_data(contact_data: Dict[str, Any]) -> Dict[str, str]: def validate_contact_data(contact_data: Dict[str, Any]) -> Dict[str, str]:
""" """
@ -28,8 +28,8 @@ def validate_contact_data(contact_data: Dict[str, Any]) -> Dict[str, str]:
errors["email"] = "Email is required" errors["email"] = "Email is required"
else: else:
try: try:
email_validator.validate_email(contact_data["email"]) EmailStr.validate(contact_data["email"])
except email_validator.EmailNotValidError: except ValidationError:
errors["email"] = "Invalid email format - please provide a valid email address" errors["email"] = "Invalid email format - please provide a valid email address"
# Validate message # Validate message
@ -52,23 +52,23 @@ def create_contact(db: Session, contact_data: ContactCreate) -> Contact:
Contact: The newly created contact object. Contact: The newly created contact object.
Raises: Raises:
HTTPException: If there are validation errors. HTTPException: If there are validation errors with specific field details.
""" """
# Validate data try:
validation_errors = validate_contact_data(contact_data.dict()) # ContactCreate schema will handle validation
if validation_errors: validated_data = contact_data.dict()
db_contact = Contact(**validated_data)
db.add(db_contact)
db.commit()
db.refresh(db_contact)
return db_contact
except ValidationError as e:
errors = {error["loc"][0]: error["msg"] for error in e.errors()}
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
detail=validation_errors detail=errors
) )
# Create new contact
db_contact = Contact(**contact_data.dict())
db.add(db_contact)
db.commit()
db.refresh(db_contact)
return db_contact
def format_contact_response(contact: Contact) -> ContactSchema: def format_contact_response(contact: Contact) -> ContactSchema:
""" """
Formats a contact database object into the response schema. Formats a contact database object into the response schema.
@ -83,32 +83,51 @@ def format_contact_response(contact: Contact) -> ContactSchema:
def sanitize_contact_input(contact_data: Dict[str, Any]) -> Dict[str, Any]: def sanitize_contact_input(contact_data: Dict[str, Any]) -> Dict[str, Any]:
""" """
Sanitizes contact form input data with enhanced validation. Sanitizes contact form input data with enhanced validation for required fields.
Args: Args:
contact_data (Dict[str, Any]): Raw contact form data. contact_data (Dict[str, Any]): Raw contact form data.
Returns: Returns:
Dict[str, Any]: Sanitized contact form data. Dict[str, Any]: Sanitized contact form data.
Raises:
HTTPException: If required fields are missing or invalid with specific field details.
""" """
errors = {}
sanitized = {} sanitized = {}
# Sanitize and validate name
if "name" in contact_data: if "name" in contact_data:
sanitized["name"] = contact_data["name"].strip() sanitized["name"] = contact_data["name"].strip()
if not sanitized["name"]:
errors["name"] = "Name cannot be empty or consist of only whitespace"
else:
errors["name"] = "Name is required"
# Sanitize and validate email
if "email" in contact_data: if "email" in contact_data:
sanitized["email"] = contact_data["email"].lower().strip() email = contact_data["email"].lower().strip()
try:
EmailStr.validate(email)
sanitized["email"] = email
except ValidationError:
errors["email"] = "Invalid email format"
else:
errors["email"] = "Email is required"
# Sanitize and validate message
if "message" in contact_data: if "message" in contact_data:
sanitized["message"] = contact_data["message"].strip() sanitized["message"] = contact_data["message"].strip()
if not sanitized["message"]:
# Ensure all required fields are present errors["message"] = "Message cannot be empty or consist of only whitespace"
required_fields = ["name", "email", "message"] else:
for field in required_fields: errors["message"] = "Message is required"
if field not in sanitized or not sanitized[field]:
raise HTTPException( if errors:
status_code=status.HTTP_400_BAD_REQUEST, raise HTTPException(
detail=f"{field.capitalize()} is required" status_code=status.HTTP_400_BAD_REQUEST,
) detail=errors
)
return sanitized return sanitized

View File

@ -8,8 +8,8 @@ class Contact(Base):
__tablename__ = "contacts" __tablename__ = "contacts"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String, nullable=False) name = Column(String, nullable=False, info={'min_length': 1, 'description': 'Contact name'})
email = Column(String, nullable=False, index=True) email = Column(String, nullable=False, index=True, info={'description': 'Contact email address'})
message = Column(Text, nullable=False) message = Column(Text, nullable=False, info={'min_length': 1, 'description': 'Contact message'})
created_at = Column(DateTime, default=func.now()) created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now())