from typing import Any from pydantic import BaseModel, Field from app.core.constants import ActorValue from app.modules.legacy_mysql.constants import LegacyQueryName class ReadonlyQueryRequest(BaseModel): """Allowlisted readonly query request for the legacy MySQL connection.""" query_name: str | None = Field( default=LegacyQueryName.PROJECTS, description="Configured readonly query name.", ) sql: str | None = Field( default=None, description="Deprecated: must exactly match a configured readonly query.", ) params: dict[str, Any] = Field(default_factory=dict) limit: int = Field(default=100, ge=1, le=500) class QueryResult(BaseModel): """Tabular query result returned as JSON rows.""" columns: list[str] rows: list[dict[str, Any]] row_count: int class LegacyProjectRecord(BaseModel): """Raw project row from the legacy project system.""" data: dict[str, Any] class LegacyProjectSyncRequest(BaseModel): """Request body for syncing legacy projects into the internal ledger.""" source_query: str | None = Field( default=None, description="Deprecated: must exactly match a configured readonly query.", ) source_query_name: str | None = Field( default=LegacyQueryName.PROJECTS, description="Configured readonly query name for project sync.", ) field_map: dict[str, str] = Field( default_factory=dict, description=( "Map internal project fields to legacy row fields, " "e.g. {'name': 'project_name'}." ), ) limit: int = Field(default=100, ge=1, le=500) dry_run: bool = True actor: str = ActorValue.API class LegacyProjectSyncResult(BaseModel): """Summary of a legacy project sync operation.""" dry_run: bool created: int updated: int skipped: int sync_run_code: str | None = None items: list[dict[str, Any]]