"""Shared constants and Pydantic request models for the documents API package. CODE-08: Single definition of _CLOUD_PROVIDERS, UploadUrlRequest, and DocumentPatch. These are imported by upload.py and crud.py — never duplicated. T-05-06-01: _CLOUD_PROVIDERS is an allowlist frozenset; target_backend validated against it (never against user-supplied strings). T-05-09-01: DocumentPatch fields declared explicitly — mass assignment prevented. T-05-09-02: filename_no_path_separators validator preserved verbatim (path traversal defense at the API boundary — D-11 analysis: stays in Pydantic model). """ from __future__ import annotations import uuid from typing import Optional from pydantic import BaseModel, Field, field_validator # Valid cloud backend slugs (T-05-06-01: validated against allowlist, not user-supplied string) _CLOUD_PROVIDERS = frozenset({"google_drive", "onedrive", "nextcloud", "webdav"}) class UploadUrlRequest(BaseModel): filename: str content_type: str class DocumentPatch(BaseModel): """Pydantic model for PATCH /api/documents/{doc_id}. Optional fields — model_fields_set distinguishes "not provided" from "set to null". At least one field must be present in model_fields_set (enforced in the handler). T-05-09-01: explicit field declaration prevents mass assignment. T-05-09-02: only filename and folder_id are accepted — no other fields can be set. """ filename: Optional[str] = Field(None, min_length=1, max_length=255) folder_id: Optional[uuid.UUID] = None @field_validator("filename") @classmethod def filename_no_path_separators(cls, v: Optional[str]) -> Optional[str]: if v is not None and ("/" in v or "\\" in v): raise ValueError("filename must not contain path separators") return v