"""Schemas for gateway CRUD and template-sync API payloads.""" from __future__ import annotations from datetime import datetime from uuid import UUID from pydantic import field_validator from sqlmodel import Field, SQLModel RUNTIME_ANNOTATION_TYPES = (datetime, UUID) class GatewayBase(SQLModel): """Shared gateway fields used across create/read payloads.""" name: str url: str workspace_root: str allow_insecure_tls: bool = False disable_device_pairing: bool = False class GatewayCreate(GatewayBase): """Payload for creating a gateway configuration.""" token: str | None = None @field_validator("token", mode="before") @classmethod def normalize_token(cls, value: object) -> str | None | object: """Normalize empty/whitespace tokens to `None`.""" if value is None: return None if isinstance(value, str): value = value.strip() return value or None return value class GatewayUpdate(SQLModel): """Payload for partial gateway updates.""" name: str | None = None url: str | None = None token: str | None = None workspace_root: str | None = None allow_insecure_tls: bool | None = None disable_device_pairing: bool | None = None @field_validator("token", mode="before") @classmethod def normalize_token(cls, value: object) -> str | None | object: """Normalize empty/whitespace tokens to `None`.""" if value is None: return None if isinstance(value, str): value = value.strip() return value or None return value class GatewayRead(GatewayBase): """Gateway payload returned from read endpoints.""" id: UUID organization_id: UUID token: str | None = None created_at: datetime updated_at: datetime class GatewayTemplatesSyncError(SQLModel): """Per-agent error entry from a gateway template sync operation.""" agent_id: UUID | None = None agent_name: str | None = None board_id: UUID | None = None message: str class GatewayTemplatesSyncResult(SQLModel): """Summary payload returned by gateway template sync endpoints.""" gateway_id: UUID include_main: bool reset_sessions: bool agents_updated: int agents_skipped: int main_updated: bool errors: list[GatewayTemplatesSyncError] = Field(default_factory=list) class GatewayAgentImportCandidate(SQLModel): """One gateway runtime agent candidate discovered for import.""" gateway_agent_id: str gateway_agent_name: str | None = None session_key: str | None = None existing_agent_id: UUID | None = None importable: bool inferred_board_id: UUID | None = None inferred_is_board_lead: bool = False class GatewayAgentImportPreviewResponse(SQLModel): """Discovery preview response for importing existing gateway agents.""" gateway_id: UUID discovered_count: int importable_count: int already_tracked_count: int candidates: list[GatewayAgentImportCandidate] = Field(default_factory=list) class GatewayAgentImportRequest(SQLModel): """Request payload listing gateway runtime agents to import.""" gateway_agent_ids: list[str] = Field(default_factory=list, min_length=1) reconcile_after_import: bool = False rotate_tokens: bool = True reset_sessions: bool = True force_bootstrap: bool = True class GatewayAgentReconcileError(SQLModel): """One reconciliation error for an imported agent.""" agent_id: UUID | None = None message: str class GatewayAgentImportReconcileSummary(SQLModel): """Summary for optional post-import reconcile execution.""" attempted: int updated: int skipped: int errors: list[GatewayAgentReconcileError] = Field(default_factory=list) class GatewayAgentImportResponse(SQLModel): """Import summary response for existing gateway runtime agents.""" gateway_id: UUID imported_count: int skipped_count: int imported_agent_ids: list[UUID] = Field(default_factory=list) skipped_gateway_agent_ids: list[str] = Field(default_factory=list) reconcile: GatewayAgentImportReconcileSummary | None = None