"""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 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 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 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) last_sync_at: datetime | None last_sync_error: str | None created_at: datetime updated_at: datetime