Skip to content
Closed
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
53 changes: 48 additions & 5 deletions src/sentry/integrations/source_code_management/metrics.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from collections.abc import Mapping
from enum import StrEnum
from typing import Any
from typing import Any, Generic, TypeVar

from attr import dataclass
from attr import dataclass, field

from sentry.integrations.base import IntegrationDomain
from sentry.integrations.utils.metrics import IntegrationEventLifecycleMetric
Expand Down Expand Up @@ -70,18 +70,36 @@ class SCMIntegrationInteractionType(StrEnum):
GET_REPO_TREE = "get_repo_tree"


class SCMIntegrationMilestoneType(StrEnum):
"""
Internal operations performed while delivering an SCM integration feature.

Values remain in SCMIntegrationInteractionType until their callers migrate to
SCMIntegrationMilestoneEvent.
"""

CHECK_FILE = "check_file"
GET = "get"
GET_RATE_LIMIT = "get_rate_limit"
GET_REPOSITORIES = "get_repositories"
GET_REPO_TREE = "get_repo_tree"


class SCMResolutionHaltReason(StrEnum):
GROUP_LINK_ALREADY_EXISTS = "group_link_already_exists"
ISSUE_ALREADY_RESOLVED = "issue_already_resolved"


SCMInteractionType = TypeVar("SCMInteractionType", bound=StrEnum)


@dataclass
class SCMIntegrationInteractionEvent(IntegrationEventLifecycleMetric):
class _SCMIntegrationEvent(IntegrationEventLifecycleMetric, Generic[SCMInteractionType]):
"""
An instance to be recorded of an SCM integration feature call.
Shared dimensions for an SCM integration lifecycle event.
"""

interaction_type: SCMIntegrationInteractionType
interaction_type: SCMInteractionType
provider_key: str
integration_id: int | None = None
organization_id: int | None = None
Expand All @@ -105,6 +123,31 @@ def get_extras(self) -> Mapping[str, Any]:
}


@dataclass
class SCMIntegrationInteractionEvent(_SCMIntegrationEvent[SCMIntegrationInteractionType]):
"""
An instance to be recorded of an SCM integration feature call.
"""


@dataclass
class SCMIntegrationMilestoneEvent(_SCMIntegrationEvent[SCMIntegrationMilestoneType]):
"""
An internal operation performed while delivering an SCM integration feature.
"""

parent_interaction_type: SCMIntegrationInteractionType = field(kw_only=True)

def get_metrics_domain(self) -> str:
return "milestone"

def get_metric_tags(self) -> Mapping[str, str]:
return {
**super().get_metric_tags(),
"parent_interaction_type": str(self.parent_interaction_type),
}


@dataclass
class CommitContextIntegrationInteractionEvent(SCMIntegrationInteractionEvent):
"""
Expand Down
4 changes: 4 additions & 0 deletions src/sentry/metrics/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"integrations.slo.success",
"integrations.slo.halted",
"integrations.slo.failure",
"integrations.milestone.started",
"integrations.milestone.success",
"integrations.milestone.halted",
"integrations.milestone.failure",
]
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from sentry.integrations.source_code_management.metrics import (
SCMIntegrationInteractionEvent,
SCMIntegrationInteractionType,
SCMIntegrationMilestoneEvent,
SCMIntegrationMilestoneType,
)
from sentry.integrations.types import EventLifecycleOutcome
from sentry.models.repository import Repository
Expand Down Expand Up @@ -47,6 +49,22 @@ def test_scm_interaction_event_normalizes_provider(
}


def test_scm_milestone_event_assigns_metric_key_and_tags() -> None:
event = SCMIntegrationMilestoneEvent(
interaction_type=SCMIntegrationMilestoneType.CHECK_FILE,
parent_interaction_type=SCMIntegrationInteractionType.GET_STACKTRACE_LINK,
provider_key="integrations:github",
)

assert event.get_metric_key(EventLifecycleOutcome.STARTED) == "integrations.milestone.started"
assert event.get_metric_tags() == {
"integration_domain": "source_code_management",
"integration_name": "github",
"interaction_type": "check_file",
"parent_interaction_type": "get_stacktrace_link",
}


class MockCommitContextIntegration(CommitContextIntegration):
"""Mock implementation for testing"""

Expand Down
11 changes: 11 additions & 0 deletions tests/sentry/metrics/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ def test_filter_tags_prod() -> None:
}


def test_filter_tags_allows_milestone_integration_id() -> None:
tags = {
"integration_id": 42,
"interaction_type": "check_file",
"parent_interaction_type": "get_stacktrace_link",
}

with override_settings(SENTRY_METRICS_DISALLOW_BAD_TAGS=True):
assert _filter_tags("integrations.milestone.started", tags) == tags


def test_global() -> None:
assert get_current_global_tags() == {}

Expand Down
Loading