# 05 - Data Models & Validation (`app/models/tenant.py`)

Is document me hum `app/models/tenant.py` file ko line-by-line samjhenge aur dekhenge ki Pydantic data validation aur normalization kaise karta hai.

---

## 1. Why Data Validation Matters? (The "WHY")

Client se aane wala input chaotic aur malicious ho sakta hai:
- User display name empty bhej sakta hai (`""`).
- User domain format me `HTTPS://MYSTORE.COM/blog/index.html` full URL bhej sakta hai jabki hume clean domain `mystore.com` chahiye.
- Internal database system fields (jaise `_id` ya sensitive metadata) client ko direct expose nahi hone chahiye.

### Solution: Separate Request and Response Schemas
- **Request Model (`TenantCreate`):** Strict rules lagata hai ki client kya input bhej sakta hai.
- **Response Model (`TenantResponse`):** Guarantee karta hai ki API client ko kaun kaun se fields aur format me data reply me bhejegi.

---

## 2. Line-by-Line Code Breakdown of `app/models/tenant.py`

Here is the exact code in `app/models/tenant.py`:

```python
 1: from datetime import datetime
 2: from pydantic import BaseModel, Field, field_validator
 3: 
 4: class TenantCreate(BaseModel):
 5:     domain: str = Field(..., description="Domain name of the host site (e.g. example.com or localhost:8000)")
 6:     name: str = Field(..., min_length=1, max_length=100, description="Display name for the website/tenant")
 7: 
 8:     @field_validator("domain")
 9:     @classmethod
10:     def clean_domain(cls, v: str) -> str:
11:         domain = v.strip().lower()
12:         # Strip protocols if user passes full URL
13:         if domain.startswith("http://"):
14:             domain = domain[7:]
15:         elif domain.startswith("https://"):
16:             domain = domain[8:]
17:         # Strip trailing slash or path
18:         domain = domain.split("/")[0]
19:         if not domain:
20:             raise ValueError("Domain cannot be empty")
21:         return domain
22: 
23: class TenantResponse(BaseModel):
24:     siteId: str
25:     domain: str
26:     name: str
27:     createdAt: datetime
```

---

### Line 1-2: Imports
```python
from datetime import datetime
from pydantic import BaseModel, Field, field_validator
```
- `BaseModel`: Base class from Pydantic for data validation models.
- `Field`: Extra metadata aur constraint rules (min_length, max_length, description) set karne ke liye utility.
- `field_validator`: Custom transformation / validation logic run karne wala decorator.

---

### Line 4-6: `TenantCreate` Request Schema
```python
class TenantCreate(BaseModel):
    domain: str = Field(..., description="Domain name of the host site...")
    name: str = Field(..., min_length=1, max_length=100, description="Display name...")
```
- `...` (Ellipsis) signifies that `domain` and `name` are **required** fields.
- `min_length=1, max_length=100`: Ensures name is non-empty string and not ridiculously long.

---

### Line 8-21: Domain Normalization Custom Validator
```python
    @field_validator("domain")
    @classmethod
    def clean_domain(cls, v: str) -> str:
        domain = v.strip().lower()
        if domain.startswith("http://"):
            domain = domain[7:]
        elif domain.startswith("https://"):
            domain = domain[8:]
        domain = domain.split("/")[0]
        if not domain:
            raise ValueError("Domain cannot be empty")
        return domain
```

#### How this validator cleans incoming domains:
Suppose the user sends: `"  HTTP://MyStore.com/shop/page1  "`

1. `v.strip().lower()` -> Removes outer whitespace & converts to lowercase: `"http://mystore.com/shop/page1"`
2. `domain.startswith("http://")` -> Strips prefix: `"mystore.com/shop/page1"`
3. `domain.split("/")[0]` -> Splits by slash and takes domain part only: `"mystore.com"`
4. Checks if domain is not empty, then returns clean normalized domain `"mystore.com"`.

If the cleaned domain is empty, it raises `ValueError`, causing FastAPI to automatically return a `422 Unprocessable Entity` response.

---

### Line 23-27: `TenantResponse` Response Schema
```python
class TenantResponse(BaseModel):
    siteId: str
    domain: str
    name: str
    createdAt: datetime
```
- When returning data to the client, FastAPI converts the MongoDB document into this schema.
- Notice that internal MongoDB `_id` (ObjectId) is **excluded** from `TenantResponse`. Only `siteId`, `domain`, `name`, and `createdAt` are sent to the client.

---

## 3. Summary Checkpoint
- `TenantCreate` validates incoming requests & normalizes domain names.
- Custom `@field_validator` cleans up dirty URLs into standardized domain strings.
- `TenantResponse` guarantees clean and secure JSON response structure.
