"""Schemas for Forgejo repository CRUD 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 ForgejoRepositoryBase(SQLModel): """Shared repository fields used across create/read payloads.""" owner: str repo: str display_name: str = "" default_branch: str = "main" active: bool = True @field_validator("owner", "repo", mode="before") @classmethod def normalize_strings(cls, value: object) -> str | None | object: """Normalize whitespace in owner and repo.""" if value is None: return None if isinstance(value, str): value = value.strip() if not value: return None return value return value class ForgejoRepositoryCreate(ForgejoRepositoryBase): """Payload for creating a Forgejo repository tracked configuration.""" connection_id: UUID webhook_secret: str | None = None @field_validator("webhook_secret", mode="before") @classmethod def normalize_webhook_secret(cls, value: object) -> str | None | object: """Normalize empty webhook secrets to null.""" if value is None: return None if isinstance(value, str): value = value.strip() return value or None return value class ForgejoRepositoryUpdate(SQLModel): """Payload for partial Forgejo repository updates.""" connection_id: UUID | None = None owner: str | None = None repo: str | None = None display_name: str | None = None default_branch: str | None = None active: bool | None = None webhook_secret: str | None = None @field_validator("owner", "repo", mode="before") @classmethod def normalize_strings(cls, value: object) -> str | None | object: """Normalize whitespace in owner and repo.""" if value is None: return None if isinstance(value, str): value = value.strip() if not value: return None return value return value @field_validator("webhook_secret", mode="before") @classmethod def normalize_webhook_secret(cls, value: object) -> str | None | object: """Normalize empty webhook secrets to null.""" if value is None: return None if isinstance(value, str): value = value.strip() return value or None return value class ForgejoRepositoryConnectionInfo(SQLModel): """Safe connection metadata included in repository read responses.""" id: UUID organization_id: UUID name: str base_url: str has_token: bool token_last_eight: str | None active: bool class ForgejoRepositoryBoardSummary(SQLModel): """Small board summary embedded in repository settings responses.""" id: UUID name: str class MassImportRequest(SQLModel): """Optional body for the mass import endpoint.""" repository_ids: list[UUID] | None = None class MassImportRepoResult(SQLModel): """Per-repository result from a mass import run.""" repository_id: UUID name: str created: int = 0 updated: int = 0 stale_closed: int = 0 open: int = 0 closed: int = 0 total: int = 0 error: str | None = None class MassImportRunRead(SQLModel): """Persisted import run summary.""" id: UUID organization_id: UUID requested_by_user_id: UUID | None = None repository_ids: list[UUID] = Field(default_factory=list) results: list[MassImportRepoResult] = Field(default_factory=list) total_created: int = 0 total_updated: int = 0 total_stale_closed: int = 0 succeeded: int = 0 failed: int = 0 started_at: datetime finished_at: datetime | None = None duration_ms: int | None = None created_at: datetime class MassImportResponse(SQLModel): """Aggregate result from a mass import across all requested repositories.""" results: list[MassImportRepoResult] total_created: int = 0 total_updated: int = 0 total_stale_closed: int = 0 succeeded: int = 0 failed: int = 0 run: MassImportRunRead | None = None class ForgejoRepositoryRead(ForgejoRepositoryBase): """Repository payload returned from read endpoints.""" id: UUID organization_id: UUID connection_id: UUID connection: ForgejoRepositoryConnectionInfo has_webhook_secret: bool = False description: str | None = None open_issues_count: int = 0 is_archived: bool = False topics: list[str] = Field(default_factory=list) labels: list[dict[str, object]] = Field(default_factory=list) linked_boards: list[ForgejoRepositoryBoardSummary] = Field(default_factory=list) last_sync_at: datetime | None last_sync_error: str | None created_at: datetime updated_at: datetime