# [DEF:GitSchemas:Module] # # @COMPLEXITY: 1 # @SEMANTICS: git, schemas, pydantic, api, contracts # @PURPOSE: Defines Pydantic models for the Git integration API layer. # @LAYER: API # @RELATION: DEPENDS_ON -> backend.src.models.git # # @INVARIANT: All schemas must be compatible with the FastAPI router. from pydantic import BaseModel, Field from typing import Any, Dict, List, Optional from datetime import datetime from src.models.git import GitProvider, GitStatus, SyncStatus # [DEF:GitServerConfigBase:Class] # @COMPLEXITY: 1 # @PURPOSE: Base schema for Git server configuration attributes. class GitServerConfigBase(BaseModel): name: str = Field(..., description="Display name for the Git server") provider: GitProvider = Field(..., description="Git provider (GITHUB, GITLAB, GITEA)") url: str = Field(..., description="Server base URL") pat: str = Field(..., description="Personal Access Token") pat: str = Field(..., description="Personal Access Token") default_repository: Optional[str] = Field(None, description="Default repository path (org/repo)") default_branch: Optional[str] = Field("main", description="Default branch logic/name") # [/DEF:GitServerConfigBase:Class] # [DEF:GitServerConfigUpdate:Class] # @PURPOSE: Schema for updating an existing Git server configuration. class GitServerConfigUpdate(BaseModel): name: Optional[str] = Field(None, description="Display name for the Git server") provider: Optional[GitProvider] = Field(None, description="Git provider (GITHUB, GITLAB, GITEA)") url: Optional[str] = Field(None, description="Server base URL") pat: Optional[str] = Field(None, description="Personal Access Token") default_repository: Optional[str] = Field(None, description="Default repository path (org/repo)") default_branch: Optional[str] = Field(None, description="Default branch logic/name") # [/DEF:GitServerConfigUpdate:Class] # [DEF:GitServerConfigCreate:Class] # @PURPOSE: Schema for creating a new Git server configuration. class GitServerConfigCreate(GitServerConfigBase): """Schema for creating a new Git server configuration.""" config_id: Optional[str] = Field(None, description="Optional config ID, useful for testing an existing config without sending its full PAT") # [/DEF:GitServerConfigCreate:Class] # [DEF:GitServerConfigSchema:Class] # @PURPOSE: Schema for representing a Git server configuration with metadata. class GitServerConfigSchema(GitServerConfigBase): """Schema for representing a Git server configuration with metadata.""" id: str status: GitStatus last_validated: datetime class Config: from_attributes = True # [/DEF:GitServerConfigSchema:Class] # [DEF:GitRepositorySchema:Class] # @PURPOSE: Schema for tracking a local Git repository linked to a dashboard. class GitRepositorySchema(BaseModel): """Schema for tracking a local Git repository linked to a dashboard.""" id: str dashboard_id: int config_id: str remote_url: str local_path: str current_branch: str sync_status: SyncStatus class Config: from_attributes = True # [/DEF:GitRepositorySchema:Class] # [DEF:BranchSchema:Class] # @PURPOSE: Schema for representing a Git branch metadata. class BranchSchema(BaseModel): """Schema for representing a Git branch.""" name: str commit_hash: str is_remote: bool last_updated: datetime # [/DEF:BranchSchema:Class] # [DEF:CommitSchema:Class] # @PURPOSE: Schema for representing Git commit details. class CommitSchema(BaseModel): """Schema for representing a Git commit.""" hash: str author: str email: str timestamp: datetime message: str files_changed: List[str] # [/DEF:CommitSchema:Class] # [DEF:BranchCreate:Class] # @PURPOSE: Schema for branch creation requests. class BranchCreate(BaseModel): """Schema for branch creation requests.""" name: str from_branch: str # [/DEF:BranchCreate:Class] # [DEF:BranchCheckout:Class] # @PURPOSE: Schema for branch checkout requests. class BranchCheckout(BaseModel): """Schema for branch checkout requests.""" name: str # [/DEF:BranchCheckout:Class] # [DEF:CommitCreate:Class] # @PURPOSE: Schema for staging and committing changes. class CommitCreate(BaseModel): """Schema for staging and committing changes.""" message: str files: List[str] # [/DEF:CommitCreate:Class] # [DEF:ConflictResolution:Class] # @PURPOSE: Schema for resolving merge conflicts. class ConflictResolution(BaseModel): """Schema for resolving merge conflicts.""" file_path: str resolution: str = Field(pattern="^(mine|theirs|manual)$") content: Optional[str] = None # [/DEF:ConflictResolution:Class] # [DEF:MergeStatusSchema:Class] # @PURPOSE: Schema representing unfinished merge status for repository. class MergeStatusSchema(BaseModel): has_unfinished_merge: bool repository_path: str git_dir: str current_branch: str merge_head: Optional[str] = None merge_message_preview: Optional[str] = None conflicts_count: int = 0 # [/DEF:MergeStatusSchema:Class] # [DEF:MergeConflictFileSchema:Class] # @PURPOSE: Schema describing one conflicted file with optional side snapshots. class MergeConflictFileSchema(BaseModel): file_path: str mine: Optional[str] = None theirs: Optional[str] = None # [/DEF:MergeConflictFileSchema:Class] # [DEF:MergeResolveRequest:Class] # @PURPOSE: Request schema for resolving one or multiple merge conflicts. class MergeResolveRequest(BaseModel): resolutions: List[ConflictResolution] = Field(default_factory=list) # [/DEF:MergeResolveRequest:Class] # [DEF:MergeContinueRequest:Class] # @PURPOSE: Request schema for finishing merge with optional explicit commit message. class MergeContinueRequest(BaseModel): message: Optional[str] = None # [/DEF:MergeContinueRequest:Class] # [DEF:DeploymentEnvironmentSchema:Class] # @PURPOSE: Schema for representing a target deployment environment. class DeploymentEnvironmentSchema(BaseModel): """Schema for representing a target deployment environment.""" id: str name: str superset_url: str is_active: bool class Config: from_attributes = True # [/DEF:DeploymentEnvironmentSchema:Class] # [DEF:DeployRequest:Class] # @PURPOSE: Schema for dashboard deployment requests. class DeployRequest(BaseModel): """Schema for deployment requests.""" environment_id: str # [/DEF:DeployRequest:Class] # [DEF:RepoInitRequest:Class] # @PURPOSE: Schema for repository initialization requests. class RepoInitRequest(BaseModel): """Schema for repository initialization requests.""" config_id: str remote_url: str # [/DEF:RepoInitRequest:Class] # [DEF:RepositoryBindingSchema:Class] # @PURPOSE: Schema describing repository-to-config binding and provider metadata. class RepositoryBindingSchema(BaseModel): dashboard_id: int config_id: str provider: GitProvider remote_url: str local_path: str # [/DEF:RepositoryBindingSchema:Class] # [DEF:RepoStatusBatchRequest:Class] # @PURPOSE: Schema for requesting repository statuses for multiple dashboards in a single call. class RepoStatusBatchRequest(BaseModel): dashboard_ids: List[int] = Field(default_factory=list, description="Dashboard IDs to resolve repository statuses for") # [/DEF:RepoStatusBatchRequest:Class] # [DEF:RepoStatusBatchResponse:Class] # @PURPOSE: Schema for returning repository statuses keyed by dashboard ID. class RepoStatusBatchResponse(BaseModel): statuses: Dict[str, Dict[str, Any]] # [/DEF:RepoStatusBatchResponse:Class] # [DEF:GiteaRepoSchema:Class] # @PURPOSE: Schema describing a Gitea repository. class GiteaRepoSchema(BaseModel): name: str full_name: str private: bool = False clone_url: Optional[str] = None html_url: Optional[str] = None ssh_url: Optional[str] = None default_branch: Optional[str] = None # [/DEF:GiteaRepoSchema:Class] # [DEF:GiteaRepoCreateRequest:Class] # @PURPOSE: Request schema for creating a Gitea repository. class GiteaRepoCreateRequest(BaseModel): name: str = Field(..., min_length=1, max_length=255) private: bool = True description: Optional[str] = None auto_init: bool = True default_branch: Optional[str] = "main" # [/DEF:GiteaRepoCreateRequest:Class] # [DEF:RemoteRepoSchema:Class] # @PURPOSE: Provider-agnostic remote repository payload. class RemoteRepoSchema(BaseModel): provider: GitProvider name: str full_name: str private: bool = False clone_url: Optional[str] = None html_url: Optional[str] = None ssh_url: Optional[str] = None default_branch: Optional[str] = None # [/DEF:RemoteRepoSchema:Class] # [DEF:RemoteRepoCreateRequest:Class] # @PURPOSE: Provider-agnostic repository creation request. class RemoteRepoCreateRequest(BaseModel): name: str = Field(..., min_length=1, max_length=255) private: bool = True description: Optional[str] = None auto_init: bool = True default_branch: Optional[str] = "main" # [/DEF:RemoteRepoCreateRequest:Class] # [DEF:PromoteRequest:Class] # @PURPOSE: Request schema for branch promotion workflow. class PromoteRequest(BaseModel): from_branch: str = Field(..., min_length=1, max_length=255) to_branch: str = Field(..., min_length=1, max_length=255) mode: str = Field(default="mr", pattern="^(mr|direct)$") title: Optional[str] = None description: Optional[str] = None reason: Optional[str] = None draft: bool = False remove_source_branch: bool = False # [/DEF:PromoteRequest:Class] # [DEF:PromoteResponse:Class] # @PURPOSE: Response schema for promotion operation result. class PromoteResponse(BaseModel): mode: str from_branch: str to_branch: str status: str url: Optional[str] = None reference_id: Optional[str] = None policy_violation: bool = False # [/DEF:PromoteResponse:Class] # [/DEF:GitSchemas:Module]