Phase 2 Task 29 — public Talk-to-Sales submission endpoint. - New POST /api/v1/sales-leads (public, no auth, rate-limited 5/hour per IP). - Inserts a sales_leads row, fires best-effort notification email and PostHog server-side capture; failures are logged but never fail the request. - New EmailService.send_sales_lead_notification static method. - New SALES_LEAD_RECIPIENT_EMAIL setting (defaults to sales@resolutionflow.com). - Schemas: SalesLeadCreate / SalesLeadCreateResponse with literal source enum. - Tests: happy path (row + email), email-failure resilience, and rate-limit enforcement (re-enables the slowapi limiter for the rate-limit assertion since DEBUG=true disables it by default in tests). PostHog server-side instrumentation point is wired in but no-ops gracefully until app.core.analytics.posthog exists — turning it on is a one-line change when the backend SDK is configured. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
28 lines
872 B
Python
28 lines
872 B
Python
"""Pydantic schemas for Talk-to-Sales submissions."""
|
|
|
|
from typing import Literal, Optional
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
|
|
|
SalesLeadSource = Literal["pricing_page", "register_footer", "landing_page"]
|
|
|
|
|
|
class SalesLeadCreate(BaseModel):
|
|
"""Public Talk-to-Sales form submission."""
|
|
|
|
model_config = ConfigDict(str_strip_whitespace=True)
|
|
|
|
email: EmailStr
|
|
name: str = Field(..., min_length=1, max_length=255)
|
|
company: str = Field(..., min_length=1, max_length=255)
|
|
team_size: Optional[str] = Field(default=None, max_length=20)
|
|
message: Optional[str] = Field(default=None, max_length=5000)
|
|
source: SalesLeadSource
|
|
posthog_distinct_id: Optional[str] = Field(default=None, max_length=255)
|
|
|
|
|
|
class SalesLeadCreateResponse(BaseModel):
|
|
id: UUID
|
|
status: Literal["received"] = "received"
|