diff --git a/src/sentry/seer/explorer/tools.py b/src/sentry/seer/explorer/tools.py index 78b2206ec9f6c1..9a3cd09baa0daf 100644 --- a/src/sentry/seer/explorer/tools.py +++ b/src/sentry/seer/explorer/tools.py @@ -322,6 +322,51 @@ def get_repository_definition(*, organization_id: int, repo_full_name: str) -> d } +def _get_issue_event_timeseries( + *, + organization: Organization, + project_id: int, + issue_short_id: str, + stats_period: str = "7d", + interval: str = "6h", + per_page: int = 50, +) -> dict[str, Any] | None: + """ + Get event counts over time for an issue by calling the events-stats endpoint. + """ + + params: dict[str, Any] = { + "dataset": "issuePlatform", + "query": f"issue:{issue_short_id}", + "yAxis": "count()", + "partial": "1", + "statsPeriod": stats_period, + "interval": interval, + "per_page": per_page, + "project": project_id, + "referrer": Referrer.SEER_RPC, + } + + resp = client.get( + auth=ApiKey(organization_id=organization.id, scope_list=["org:read", "project:read"]), + user=None, + path=f"/organizations/{organization.slug}/events-stats/", + params=params, + ) + if resp.status_code != 200 or not (resp.data or {}).get("data"): + logger.warning( + "Failed to get event counts for issue", + extra={ + "organization_slug": organization.slug, + "project_id": project_id, + "issue_id": issue_short_id, + }, + ) + return None + + return {"count()": {"data": resp.data["data"]}} + + def get_issue_details( *, issue_id: str, @@ -354,12 +399,12 @@ def get_issue_details( ) return None + org_project_ids = Project.objects.filter( + organization=organization, status=ObjectStatus.ACTIVE + ).values_list("id", flat=True) + try: if issue_id.isdigit(): - org_project_ids = Project.objects.filter( - organization=organization, status=ObjectStatus.ACTIVE - ).values_list("id", flat=True) - group = Group.objects.get(project_id__in=org_project_ids, id=int(issue_id)) else: group = Group.objects.by_qualified_short_id(organization_id, issue_id) @@ -415,8 +460,15 @@ def get_issue_details( ) tags_overview = None + event_timeseries = _get_issue_event_timeseries( + organization=organization, + project_id=group.project_id, + issue_short_id=group.qualified_short_id, + ) + return { "issue": serialized_group, + "event_timeseries": event_timeseries, "tags_overview": tags_overview, "event": serialized_event, "event_id": event.event_id, diff --git a/tests/sentry/seer/explorer/test_tools.py b/tests/sentry/seer/explorer/test_tools.py index bef4bb1f45051b..e299928a264c16 100644 --- a/tests/sentry/seer/explorer/test_tools.py +++ b/tests/sentry/seer/explorer/test_tools.py @@ -711,6 +711,21 @@ def _test_get_issue_details_success( else: assert result["event_trace_id"] is None + # Verify timeseries dict structure. + timeseries = result["event_timeseries"] + assert isinstance(timeseries, dict) + assert "count()" in timeseries + assert "data" in timeseries["count()"] + assert isinstance(timeseries["count()"]["data"], list) + for item in timeseries["count()"]["data"]: + assert len(item) == 2 + assert isinstance(item[0], int) + assert isinstance(item[1], list) + assert len(item[1]) == 1 + assert isinstance(item[1][0], dict) + assert "count" in item[1][0] + assert isinstance(item[1][0]["count"], int) + def test_get_issue_details_success_int_id(self): self._test_get_issue_details_success(use_short_id=False)