Browse Source

Utilize decoupled handle query errors (#62491)

Follow up from https://github.com/getsentry/sentry/pull/62232

Now that we've decoupled handle_query_errors from the
`OrganizationEventsEndpointBase` class, we can stop extending it in a
handful of classes and instead import the helper handler
Nathan Hsieh 1 year ago
parent
commit
8729ddf901

+ 7 - 69
src/sentry/api/bases/organization_events.py

@@ -1,13 +1,12 @@
 from __future__ import annotations
 
-from contextlib import contextmanager
 from datetime import timedelta
-from typing import Any, Callable, Dict, Generator, Optional, Sequence, Tuple
+from typing import Any, Callable, Dict, Optional, Sequence, Tuple
 from urllib.parse import quote as urlquote
 
 import sentry_sdk
 from django.utils import timezone
-from rest_framework.exceptions import APIException, ParseError, ValidationError
+from rest_framework.exceptions import ParseError, ValidationError
 from rest_framework.request import Request
 from sentry_relay.consts import SPAN_STATUS_CODE_TO_NAME
 
@@ -19,13 +18,14 @@ from sentry.api.bases.organization import OrganizationEndpoint
 from sentry.api.helpers.mobile import get_readable_device_name
 from sentry.api.helpers.teams import get_teams
 from sentry.api.serializers.snuba import BaseSnubaSerializer, SnubaTSResultSerializer
-from sentry.discover.arithmetic import ArithmeticError, is_equation, strip_equation
-from sentry.exceptions import IncompatibleMetricsQuery, InvalidSearchQuery
+from sentry.api.utils import handle_query_errors
+from sentry.discover.arithmetic import is_equation, strip_equation
+from sentry.exceptions import InvalidSearchQuery
 from sentry.models.group import Group
 from sentry.models.organization import Organization
 from sentry.models.project import Project
 from sentry.models.team import Team
-from sentry.search.events.constants import DURATION_UNITS, SIZE_UNITS, TIMEOUT_ERROR_MESSAGE
+from sentry.search.events.constants import DURATION_UNITS, SIZE_UNITS
 from sentry.search.events.fields import get_function_alias
 from sentry.search.events.types import SnubaParams
 from sentry.snuba import (
@@ -206,68 +206,6 @@ class OrganizationEventsEndpointBase(OrganizationEndpoint):
                 )
         return results
 
-    @contextmanager
-    def handle_query_errors(self) -> Generator[None, None, None]:
-        try:
-            yield
-        except discover.InvalidSearchQuery as error:
-            message = str(error)
-            # Special case the project message since it has so many variants so tagging is messy otherwise
-            if message.endswith("do not exist or are not actively selected."):
-                sentry_sdk.set_tag(
-                    "query.error_reason", "Project in query does not exist or not selected"
-                )
-            else:
-                sentry_sdk.set_tag("query.error_reason", message)
-            raise ParseError(detail=message)
-        except ArithmeticError as error:
-            message = str(error)
-            sentry_sdk.set_tag("query.error_reason", message)
-            raise ParseError(detail=message)
-        except snuba.QueryOutsideRetentionError as error:
-            sentry_sdk.set_tag("query.error_reason", "QueryOutsideRetentionError")
-            raise ParseError(detail=str(error))
-        except snuba.QueryIllegalTypeOfArgument:
-            message = "Invalid query. Argument to function is wrong type."
-            sentry_sdk.set_tag("query.error_reason", message)
-            raise ParseError(detail=message)
-        except IncompatibleMetricsQuery as error:
-            message = str(error)
-            sentry_sdk.set_tag("query.error_reason", f"Metric Error: {message}")
-            raise ParseError(detail=message)
-        except snuba.SnubaError as error:
-            message = "Internal error. Please try again."
-            if isinstance(
-                error,
-                (
-                    snuba.RateLimitExceeded,
-                    snuba.QueryMemoryLimitExceeded,
-                    snuba.QueryExecutionTimeMaximum,
-                    snuba.QueryTooManySimultaneous,
-                ),
-            ):
-                sentry_sdk.set_tag("query.error_reason", "Timeout")
-                raise ParseError(detail=TIMEOUT_ERROR_MESSAGE)
-            elif isinstance(error, (snuba.UnqualifiedQueryError)):
-                sentry_sdk.set_tag("query.error_reason", str(error))
-                raise ParseError(detail=str(error))
-            elif isinstance(
-                error,
-                (
-                    snuba.DatasetSelectionError,
-                    snuba.QueryConnectionFailed,
-                    snuba.QueryExecutionError,
-                    snuba.QuerySizeExceeded,
-                    snuba.SchemaValidationError,
-                    snuba.QueryMissingColumn,
-                ),
-            ):
-                sentry_sdk.capture_exception(error)
-                message = "Internal error. Your query failed to run."
-            else:
-                sentry_sdk.capture_exception(error)
-            raise APIException(detail=message)
-
 
 class OrganizationEventsV2EndpointBase(OrganizationEventsEndpointBase):
     owner = ApiOwner.PERFORMANCE
@@ -439,7 +377,7 @@ class OrganizationEventsV2EndpointBase(OrganizationEventsEndpointBase):
         additional_query_column: Optional[str] = None,
         dataset: Optional[Any] = None,
     ) -> Dict[str, Any]:
-        with self.handle_query_errors():
+        with handle_query_errors():
             with sentry_sdk.start_span(
                 op="discover.endpoint", description="base.stats_query_creation"
             ):

+ 2 - 1
src/sentry/api/endpoints/organization_event_details.py

@@ -7,6 +7,7 @@ from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import OrganizationEventsEndpointBase
 from sentry.api.serializers import serialize
 from sentry.api.serializers.models.event import SqlFormatEventSerializer
+from sentry.api.utils import handle_query_errors
 from sentry.constants import ObjectStatus
 from sentry.models.project import Project
 
@@ -36,7 +37,7 @@ class OrganizationEventDetailsEndpoint(OrganizationEventsEndpointBase):
 
         # We return the requested event if we find a match regardless of whether
         # it occurred within the range specified
-        with self.handle_query_errors():
+        with handle_query_errors():
             event = eventstore.backend.get_event_by_id(project.id, event_id)
 
         if event is None:

+ 2 - 1
src/sentry/api/endpoints/organization_events.py

@@ -12,6 +12,7 @@ from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
 from sentry.api.paginator import GenericOffsetPaginator
+from sentry.api.utils import handle_query_errors
 from sentry.apidocs import constants as api_constants
 from sentry.apidocs.examples.discover_performance_examples import DiscoverAndPerformanceExamples
 from sentry.apidocs.parameters import GlobalParams, OrganizationParams, VisibilityParams
@@ -301,7 +302,7 @@ class OrganizationEventsEndpoint(OrganizationEventsV2EndpointBase):
                 on_demand_metrics_type=on_demand_metrics_type,
             )
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             # Don't include cursor headers if the client won't be using them
             if request.GET.get("noPagination"):
                 return Response(

+ 2 - 1
src/sentry/api/endpoints/organization_events_facets.py

@@ -9,6 +9,7 @@ from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
 from sentry.api.paginator import GenericOffsetPaginator
+from sentry.api.utils import handle_query_errors
 from sentry.search.utils import DEVICE_CLASS
 from sentry.snuba import discover
 
@@ -30,7 +31,7 @@ class OrganizationEventsFacetsEndpoint(OrganizationEventsV2EndpointBase):
 
         def data_fn(offset, limit):
             with sentry_sdk.start_span(op="discover.endpoint", description="discover_query"):
-                with self.handle_query_errors():
+                with handle_query_errors():
                     facets = discover.get_facets(
                         query=request.GET.get("query"),
                         params=params,

+ 3 - 2
src/sentry/api/endpoints/organization_events_facets_performance.py

@@ -14,6 +14,7 @@ from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
 from sentry.api.paginator import GenericOffsetPaginator
+from sentry.api.utils import handle_query_errors
 from sentry.search.events.builder import QueryBuilder
 from sentry.search.events.fields import DateArg
 from sentry.snuba import discover
@@ -123,7 +124,7 @@ class OrganizationEventsFacetsPerformanceEndpoint(OrganizationEventsFacetsPerfor
 
                 return results
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             return self.paginate(
                 request=request,
                 paginator=GenericOffsetPaginator(data_fn=data_fn),
@@ -223,7 +224,7 @@ class OrganizationEventsFacetsPerformanceHistogramEndpoint(
                 ),
             }
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             return self.paginate(
                 request=request,
                 paginator=HistogramPaginator(data_fn=data_fn),

+ 2 - 1
src/sentry/api/endpoints/organization_events_has_measurements.py

@@ -10,6 +10,7 @@ from rest_framework.response import Response
 from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
+from sentry.api.utils import handle_query_errors
 from sentry.snuba import discover
 from sentry.utils.hashlib import md5_text
 
@@ -93,7 +94,7 @@ class OrganizationEventsHasMeasurementsEndpoint(OrganizationEventsV2EndpointBase
 
         # cache miss, need to make the query again
         if has_measurements is None:
-            with self.handle_query_errors():
+            with handle_query_errors():
                 validated_data = serializer.validated_data
 
                 # generate the appropriate query for the selected type

+ 2 - 1
src/sentry/api/endpoints/organization_events_histogram.py

@@ -8,6 +8,7 @@ from sentry.api.api_owners import ApiOwner
 from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
+from sentry.api.utils import handle_query_errors
 from sentry.snuba import discover
 
 # The maximum number of array columns allowed to be queried at at time
@@ -76,7 +77,7 @@ class OrganizationEventsHistogramEndpoint(OrganizationEventsV2EndpointBase):
             if serializer.is_valid():
                 data = serializer.validated_data
 
-                with self.handle_query_errors():
+                with handle_query_errors():
                     results = dataset.histogram_query(
                         data["field"],
                         data.get("query"),

+ 3 - 2
src/sentry/api/endpoints/organization_events_meta.py

@@ -13,6 +13,7 @@ from sentry.api.event_search import parse_search_query
 from sentry.api.helpers.group_index import build_query_params_from_request
 from sentry.api.serializers import serialize
 from sentry.api.serializers.models.group import GroupSerializer
+from sentry.api.utils import handle_query_errors
 from sentry.snuba import spans_indexed, spans_metrics
 from sentry.snuba.referrer import Referrer
 
@@ -31,7 +32,7 @@ class OrganizationEventsMetaEndpoint(OrganizationEventsEndpointBase):
 
         dataset = self.get_dataset(request)
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             result = dataset.query(
                 selected_columns=["count()"],
                 params=params,
@@ -70,7 +71,7 @@ class OrganizationEventsRelatedIssuesEndpoint(OrganizationEventsEndpointBase, En
                     status=400,
                 )
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             with sentry_sdk.start_span(op="discover.endpoint", description="filter_creation"):
                 projects = self.get_projects(request, organization)
                 query_kwargs = build_query_params_from_request(

+ 2 - 1
src/sentry/api/endpoints/organization_events_root_cause_analysis.py

@@ -8,6 +8,7 @@ from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases.organization_events import OrganizationEventsEndpointBase
 from sentry.api.endpoints.organization_events_spans_performance import EventID, get_span_description
+from sentry.api.utils import handle_query_errors
 from sentry.search.events.builder import QueryBuilder
 from sentry.search.events.constants import METRICS_MAX_LIMIT
 from sentry.search.events.types import QueryBuilderConfig
@@ -260,7 +261,7 @@ class OrganizationEventsRootCauseAnalysisEndpoint(OrganizationEventsEndpointBase
 
         params = self.get_snuba_params(request, organization)
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             transaction_count_query = metrics_query(
                 ["count()"],
                 f'event.type:transaction transaction:"{transaction_name}"',

+ 2 - 1
src/sentry/api/endpoints/organization_events_span_ops.py

@@ -7,6 +7,7 @@ from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases import NoProjects, OrganizationEventsEndpointBase
 from sentry.api.paginator import GenericOffsetPaginator
+from sentry.api.utils import handle_query_errors
 from sentry.models.organization import Organization
 from sentry.search.events.builder import QueryBuilder
 from sentry.snuba.dataset import Dataset
@@ -48,7 +49,7 @@ class OrganizationEventsSpanOpsEndpoint(OrganizationEventsEndpointBase):
             results = raw_snql_query(snql_query, "api.organization-events-span-ops")
             return [SpanOp(op=row["spans_op"], count=row["count"]) for row in results["data"]]
 
-        with self.handle_query_errors():
+        with handle_query_errors():
             return self.paginate(
                 request,
                 paginator=GenericOffsetPaginator(data_fn=data_fn),

Some files were not shown because too many files changed in this diff