""" Pull Request Pydantic Schemas Validation schemas for Gitea pull request operations. """ from datetime import datetime from pydantic import BaseModel, ConfigDict, Field, field_validator from .common import GiteaUser from .issue import Label, Milestone class PRBranchInfo(BaseModel): """Pull request branch information""" model_config = ConfigDict(extra="allow") label: str ref: str sha: str repo_id: int repository: dict | None = None class PullRequest(BaseModel): """Gitea pull request model""" model_config = ConfigDict(extra="allow") id: int number: int user: GiteaUser title: str body: str | None = None labels: list[Label] = [] milestone: Milestone | None = None assignee: GiteaUser | None = None assignees: list[GiteaUser] = [] requested_reviewers: list[GiteaUser] = [] state: str # "open" or "closed" is_locked: bool = False comments: int = 0 html_url: str diff_url: str patch_url: str mergeable: bool = False merged: bool = False merged_at: datetime | None = None merge_commit_sha: str | None = None merged_by: GiteaUser | None = None base: PRBranchInfo head: PRBranchInfo merge_base: str | None = None due_date: datetime | None = None created_at: datetime updated_at: datetime closed_at: datetime | None = None allow_maintainer_edit: bool = False url: str class PRReview(BaseModel): """Pull request review""" model_config = ConfigDict(extra="allow") id: int user: GiteaUser body: str | None = None commit_id: str state: str # "PENDING", "APPROVED", "REQUEST_CHANGES", "COMMENT" html_url: str pull_request_url: str submitted_at: datetime class PRCommit(BaseModel): """Pull request commit""" model_config = ConfigDict(extra="allow") sha: str commit: dict url: str html_url: str comments_url: str author: GiteaUser | None = None committer: GiteaUser | None = None parents: list[dict] = [] class PRFile(BaseModel): """Pull request file change""" model_config = ConfigDict(extra="allow") filename: str status: str # "added", "removed", "modified", "renamed" additions: int deletions: int changes: int raw_url: str | None = None contents_url: str | None = None patch: str | None = None previous_filename: str | None = None class CreatePullRequestRequest(BaseModel): """Request to create a pull request""" model_config = ConfigDict(extra="forbid") title: str = Field(..., min_length=1, max_length=255, description="PR title") head: str = Field(..., description="Source branch") base: str = Field(..., description="Target branch") body: str | None = Field(None, description="PR description") assignee: str | None = Field(None, description="Username of assignee") assignees: list[str] | None = Field(None, description="List of assignee usernames") labels: list[int] | None = Field(None, description="List of label IDs") milestone: int | None = Field(None, description="Milestone ID") due_date: datetime | None = Field(None, description="Due date") class UpdatePullRequestRequest(BaseModel): """Request to update a pull request""" model_config = ConfigDict(extra="forbid") title: str | None = Field(None, min_length=1, max_length=255, description="PR title") body: str | None = Field(None, description="PR description") assignee: str | None = Field(None, description="Username of assignee") assignees: list[str] | None = Field(None, description="List of assignee usernames") labels: list[int] | None = Field(None, description="List of label IDs") milestone: int | None = Field(None, description="Milestone ID") state: str | None = Field(None, description="State (open/closed)") due_date: datetime | None = Field(None, description="Due date") unset_due_date: bool | None = Field(None, description="Unset due date") allow_maintainer_edit: bool | None = Field(None, description="Allow maintainer edit") @classmethod @field_validator("state") def validate_state(cls, v): if v and v not in ["open", "closed"]: raise ValueError("State must be 'open' or 'closed'") return v class MergePullRequestRequest(BaseModel): """Request to merge a pull request""" model_config = ConfigDict(extra="forbid") Do: str = Field( ..., description="Merge method (merge/rebase/rebase-merge/squash/manually-merged)" ) MergeTitleField: str | None = Field(None, description="Merge commit title") MergeMessageField: str | None = Field(None, description="Merge commit message") delete_branch_after_merge: bool | None = Field(False, description="Delete branch after merge") force_merge: bool | None = Field(False, description="Force merge even if not mergeable") head_commit_id: str | None = Field(None, description="Expected HEAD commit SHA") merge_when_checks_succeed: bool | None = Field(False, description="Auto-merge when checks pass") @classmethod @field_validator("Do") def validate_merge_method(cls, v): allowed = ["merge", "rebase", "rebase-merge", "squash", "manually-merged"] if v not in allowed: raise ValueError(f"Merge method must be one of: {', '.join(allowed)}") return v class CreateReviewRequest(BaseModel): """Request to create a review""" model_config = ConfigDict(extra="forbid") body: str | None = Field(None, description="Review comment") event: str = Field(..., description="Review event (APPROVED/REQUEST_CHANGES/COMMENT)") comments: list[dict] | None = Field(None, description="Inline review comments") @classmethod @field_validator("event") def validate_event(cls, v): allowed = ["APPROVED", "REQUEST_CHANGES", "COMMENT"] if v not in allowed: raise ValueError(f"Review event must be one of: {', '.join(allowed)}") return v class RequestReviewersRequest(BaseModel): """Request reviewers for a pull request""" model_config = ConfigDict(extra="forbid") reviewers: list[str] = Field(..., min_items=1, description="List of reviewer usernames") team_reviewers: list[str] | None = Field(None, description="List of team slugs") class PRListFilters(BaseModel): """Filters for listing pull requests""" model_config = ConfigDict(extra="forbid") state: str | None = Field("open", description="Filter by state (open/closed/all)") sort: str | None = Field( "created", description="Sort by (created/updated/comments/recentupdate)" ) labels: str | None = Field(None, description="Comma-separated label IDs") milestone: str | None = Field(None, description="Milestone name") @classmethod @field_validator("state") def validate_state(cls, v): if v and v not in ["open", "closed", "all"]: raise ValueError("State must be 'open', 'closed', or 'all'") return v @classmethod @field_validator("sort") def validate_sort(cls, v): allowed = ["created", "updated", "comments", "recentupdate"] if v and v not in allowed: raise ValueError(f"Sort must be one of: {', '.join(allowed)}") return v