Skip to content
Draft

WIP #107806

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ dependencies = [
"sentry-redis-tools>=0.5.0",
"sentry-relay>=0.9.22",
"sentry-sdk[http2]>=2.47.0",
"sentry-seer-types>=0.1.0",
"sentry-usage-accountant>=0.0.10",
# remove once there are no unmarked transitive dependencies on setuptools
"setuptools>=70.0.0",
Expand Down
198 changes: 29 additions & 169 deletions src/sentry/seer/code_review/models.py
Original file line number Diff line number Diff line change
@@ -1,171 +1,31 @@
from __future__ import annotations

from datetime import datetime
from enum import StrEnum
from typing import Literal

from pydantic import BaseModel, Field

from sentry.seer.models import SeerRepoDefinition

# =============================================================================
# Code Review Models (ported from Seer)
# =============================================================================


class CommentSeverity(StrEnum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"


class SeerCodeReviewFeature(StrEnum):
BUG_PREDICTION = "bug_prediction"


class SeerCodeReviewTrigger(StrEnum):
UNKNOWN = "unknown"
ON_COMMAND_PHRASE = "on_command_phrase"
ON_READY_FOR_REVIEW = "on_ready_for_review"
ON_NEW_COMMIT = "on_new_commit"

@classmethod
def _missing_(cls: type[SeerCodeReviewTrigger], value: object) -> SeerCodeReviewTrigger:
return cls.UNKNOWN


class SeerCodeReviewRequestType(StrEnum):
"""Request type for Seer code review requests."""

PR_REVIEW = "pr-review"
PR_CLOSED = "pr-closed"


class SeerCodeReviewConfig(BaseModel):
features: dict[SeerCodeReviewFeature, bool] = Field(default_factory=lambda: {})
trigger: SeerCodeReviewTrigger
trigger_comment_id: int | None = None
trigger_comment_type: Literal["issue_comment"] | None = None
trigger_user: str | None = None
trigger_user_id: int | None = None
trigger_at: datetime | None = None # When the trigger event occurred on GitHub
sentry_received_trigger_at: datetime | None = None # When Sentry received the webhook

def is_feature_enabled(self, feature: SeerCodeReviewFeature) -> bool:
return self.features.get(feature, False)

def get_minimum_severity_for_feature(self, feature: SeerCodeReviewFeature) -> CommentSeverity:
return CommentSeverity.MEDIUM


class BugPredictionSpecificInformation(BaseModel):
"""Information specific to bug prediction feature."""

organization_id: int
organization_slug: str


# =============================================================================
# Code Review Repo Definition Models
# =============================================================================


class SeerCodeReviewRepoDefinition(BaseModel):
"""
Repo definition for code review with required fields for Sentry.

This is a "shortened" version of SeerRepoDefinition that only includes
the fields actually sent by Sentry for code review requests.
"""

provider: str
owner: str
name: str
external_id: str
base_commit_sha: str
# Optional in base, overridden in subclasses based on request type
organization_id: int | None = None
integration_id: str | None = None


class SeerCodeReviewRepoForPrReview(SeerCodeReviewRepoDefinition):
"""
Repo definition for PR review requests.

organization_id and integration_id are optional for PR review requests,
though organization_id is typically included.
"""

pass # Inherits optional fields from base


class SeerCodeReviewRepoForPrClosed(SeerCodeReviewRepoDefinition):
"""
Repo definition for PR closed requests.

organization_id and integration_id are required for PR closed requests
to support metrics and dashboarding.
"""

organization_id: int # Override to make required
integration_id: str # Override to make required


# =============================================================================
# Code Review Request Models
# =============================================================================


class SeerCodeReviewBaseRequest(BaseModel):
repo: SeerRepoDefinition
pr_id: int
more_readable_repos: list[SeerRepoDefinition] = Field(default_factory=list)


class SeerCodeReviewRequest(SeerCodeReviewBaseRequest):
bug_prediction_specific_information: BugPredictionSpecificInformation
config: SeerCodeReviewConfig | None = None


class SeerCodeReviewRequestForPrReview(BaseModel):
"""Request model for PR review with optional organization_id and integration_id."""

repo: SeerCodeReviewRepoForPrReview
pr_id: int
more_readable_repos: list[SeerCodeReviewRepoForPrReview] = Field(default_factory=list)
bug_prediction_specific_information: BugPredictionSpecificInformation
config: SeerCodeReviewConfig | None = None
experiment_enabled: bool = False


class SeerCodeReviewRequestForPrClosed(BaseModel):
"""Request model for PR closed with required organization_id and integration_id."""

repo: SeerCodeReviewRepoForPrClosed
pr_id: int
more_readable_repos: list[SeerCodeReviewRepoForPrClosed] = Field(default_factory=list)
bug_prediction_specific_information: BugPredictionSpecificInformation
config: SeerCodeReviewConfig | None = None


class SeerCodeReviewTaskRequest(BaseModel):
data: SeerCodeReviewRequest
external_owner_id: str
request_type: SeerCodeReviewRequestType


class SeerCodeReviewTaskRequestForPrReview(BaseModel):
"""Task request wrapper for PR review."""

data: SeerCodeReviewRequestForPrReview
external_owner_id: str
request_type: SeerCodeReviewRequestType


class SeerCodeReviewTaskRequestForPrClosed(BaseModel):
"""Task request wrapper for PR closed."""

data: SeerCodeReviewRequestForPrClosed
external_owner_id: str
request_type: SeerCodeReviewRequestType
# Import shared models from sentry-seer-types package (v1 for Pydantic v1 compatibility)
from sentry_seer_types.v1.code_review import (
BugPredictionSpecificInformation,
SeerCodeReviewConfig,
SeerCodeReviewFeature,
SeerCodeReviewRequestForPrClosed,
SeerCodeReviewRequestForPrReview,
SeerCodeReviewRequestType,
SeerCodeReviewTaskRequestForPrClosed,
SeerCodeReviewTaskRequestForPrReview,
SeerCodeReviewTrigger,
)

# Re-export for backward compatibility
__all__ = [
"BugPredictionSpecificInformation",
"SeerCodeReviewConfig",
"SeerCodeReviewFeature",
"SeerCodeReviewRequestForPrClosed",
"SeerCodeReviewRequestForPrReview",
"SeerCodeReviewRequestType",
"SeerCodeReviewTaskRequestForPrClosed",
"SeerCodeReviewTaskRequestForPrReview",
"SeerCodeReviewTrigger",
]


# Note: The sentry-seer-types package provides dual Pydantic v1/v2 support.
# We import from v1.code_review since Sentry uses Pydantic v1.
Loading