Browse Source

fix(eap): Auto complete for sentry fields (#80110)

This adds auto complete support for sentry fields.
Tony Xiao 4 months ago
parent
commit
308c08b4ad

+ 150 - 103
src/sentry/api/endpoints/organization_spans_fields.py

@@ -1,3 +1,4 @@
+from abc import ABC, abstractmethod
 from datetime import timedelta
 
 import sentry_sdk
@@ -6,6 +7,7 @@ from rest_framework import serializers
 from rest_framework.exceptions import ParseError
 from rest_framework.request import Request
 from rest_framework.response import Response
+from sentry_protos.snuba.v1.trace_item_attribute_pb2 import AttributeKey
 from sentry_protos.snuba.v1alpha.endpoint_tags_list_pb2 import (
     AttributeValuesRequest,
     AttributeValuesResponse,
@@ -13,7 +15,7 @@ from sentry_protos.snuba.v1alpha.endpoint_tags_list_pb2 import (
     TraceItemAttributesResponse,
 )
 from sentry_protos.snuba.v1alpha.request_common_pb2 import RequestMeta, TraceItemName
-from sentry_protos.snuba.v1alpha.trace_item_attribute_pb2 import AttributeKey
+from sentry_protos.snuba.v1alpha.trace_item_attribute_pb2 import AttributeKey as AlphaAttributeKey
 from sentry_relay.consts import SPAN_STATUS_CODE_TO_NAME
 from snuba_sdk import Condition, Op
 
@@ -26,6 +28,9 @@ from sentry.api.event_search import translate_escape_sequences
 from sentry.api.paginator import ChainPaginator
 from sentry.api.serializers import serialize
 from sentry.api.utils import handle_query_errors
+from sentry.models.organization import Organization
+from sentry.search.eap.spans import SearchResolver
+from sentry.search.eap.types import SearchResolverConfig
 from sentry.search.events.builder.base import BaseQueryBuilder
 from sentry.search.events.builder.spans_indexed import SpansIndexedQueryBuilder
 from sentry.search.events.types import QueryBuilderConfig, SnubaParams
@@ -60,9 +65,9 @@ class OrganizationSpansFieldsEndpointSerializer(serializers.Serializer):
 
     def validate_type(self, value):
         if value == "string":
-            return AttributeKey.Type.TYPE_STRING
+            return AlphaAttributeKey.Type.TYPE_STRING
         if value == "number":
-            return AttributeKey.Type.TYPE_FLOAT
+            return AlphaAttributeKey.Type.TYPE_FLOAT
         raise NotImplementedError
 
     def validate(self, attrs):
@@ -75,7 +80,7 @@ class OrganizationSpansFieldsEndpointSerializer(serializers.Serializer):
 class OrganizationSpansFieldsEndpoint(OrganizationSpansFieldsEndpointBase):
     snuba_methods = ["GET"]
 
-    def get(self, request: Request, organization) -> Response:
+    def get(self, request: Request, organization: Organization) -> Response:
         if not features.has(
             "organizations:performance-trace-explorer", organization, actor=request.user
         ):
@@ -187,7 +192,7 @@ class OrganizationSpansFieldsEndpoint(OrganizationSpansFieldsEndpointBase):
 class OrganizationSpansFieldValuesEndpoint(OrganizationSpansFieldsEndpointBase):
     snuba_methods = ["GET"]
 
-    def get(self, request: Request, organization, key: str) -> Response:
+    def get(self, request: Request, organization: Organization, key: str) -> Response:
         if not features.has(
             "organizations:performance-trace-explorer", organization, actor=request.user
         ):
@@ -210,69 +215,27 @@ class OrganizationSpansFieldValuesEndpoint(OrganizationSpansFieldsEndpointBase):
             return Response(serializer.errors, status=400)
         serialized = serializer.validated_data
 
+        executor: BaseSpanFieldValuesAutocompletionExecutor
+
         if serialized["dataset"] == "spans" and features.has(
             "organizations:visibility-explore-dataset", organization, actor=request.user
         ):
-            start_timestamp = Timestamp()
-            start_timestamp.FromDatetime(
-                snuba_params.start_date.replace(hour=0, minute=0, second=0, microsecond=0)
-            )
-
-            end_timestamp = Timestamp()
-            end_timestamp.FromDatetime(
-                snuba_params.end_date.replace(hour=0, minute=0, second=0, microsecond=0)
-                + timedelta(days=1)
-            )
-
-            query = translate_escape_sequences(request.GET.get("query", ""))
-            rpc_request = AttributeValuesRequest(
-                meta=RequestMeta(
-                    organization_id=organization.id,
-                    cogs_category="performance",
-                    referrer=Referrer.API_SPANS_TAG_VALUES_RPC.value,
-                    project_ids=snuba_params.project_ids,
-                    start_timestamp=start_timestamp,
-                    end_timestamp=end_timestamp,
-                    trace_item_name=TraceItemName.TRACE_ITEM_NAME_EAP_SPANS,
-                ),
-                name=key,
-                value_substring_match=query,
-                limit=max_span_tag_values,
-                offset=0,
-            )
-            rpc_response = snuba_rpc.rpc(rpc_request, AttributeValuesResponse)
-
-            paginator = ChainPaginator(
-                [
-                    [
-                        TagValue(
-                            key=key,
-                            value=tag_value,
-                            times_seen=None,
-                            first_seen=None,
-                            last_seen=None,
-                        )
-                        for tag_value in rpc_response.values
-                        if tag_value
-                    ]
-                ],
-                max_limit=max_span_tag_values,
+            executor = EAPSpanFieldValuesAutocompletionExecutor(
+                organization=organization,
+                snuba_params=snuba_params,
+                key=key,
+                query=request.GET.get("query"),
+                max_span_tag_values=max_span_tag_values,
             )
-
-            return self.paginate(
-                request=request,
-                paginator=paginator,
-                on_results=lambda results: serialize(results, request.user),
-                default_per_page=max_span_tag_values,
-                max_per_page=max_span_tag_values,
+        else:
+            executor = SpanFieldValuesAutocompletionExecutor(
+                organization=organization,
+                snuba_params=snuba_params,
+                key=key,
+                query=request.GET.get("query"),
+                max_span_tag_values=max_span_tag_values,
             )
 
-        executor = SpanFieldValuesAutocompletionExecutor(
-            snuba_params=snuba_params,
-            key=key,
-            query=request.GET.get("query"),
-            max_span_tag_values=max_span_tag_values,
-        )
         tag_values = executor.execute()
 
         tag_values.sort(key=lambda tag: tag.value)
@@ -288,62 +251,27 @@ class OrganizationSpansFieldValuesEndpoint(OrganizationSpansFieldsEndpointBase):
         )
 
 
-class SpanFieldValuesAutocompletionExecutor:
-    ID_KEYS = {
-        "id",
-        "span_id",
-        "parent_span",
-        "parent_span_id",
-        "trace",
-        "trace_id",
-        "transaction.id",
-        "transaction_id",
-        "segment.id",
-        "segment_id",
-        "profile.id",
-        "profile_id",
-        "replay.id",
-        "replay_id",
-    }
-    NUMERIC_KEYS = {"span.duration", "span.self_time"}
-    TIMESTAMP_KEYS = {"timestamp"}
+class BaseSpanFieldValuesAutocompletionExecutor(ABC):
     PROJECT_SLUG_KEYS = {"project", "project.name"}
     PROJECT_ID_KEYS = {"project.id"}
-    SPAN_STATUS_KEYS = {"span.status"}
 
     def __init__(
         self,
+        organization: Organization,
         snuba_params: SnubaParams,
         key: str,
         query: str | None,
         max_span_tag_values: int,
     ):
+        self.organization = organization
         self.snuba_params = snuba_params
         self.key = key
-        self.query = query
+        self.query = query or ""
         self.max_span_tag_values = max_span_tag_values
 
+    @abstractmethod
     def execute(self) -> list[TagValue]:
-        if (
-            self.key in self.NUMERIC_KEYS
-            or self.key in self.ID_KEYS
-            or self.key in self.TIMESTAMP_KEYS
-        ):
-            return self.noop_autocomplete_function()
-
-        if self.key in self.PROJECT_ID_KEYS:
-            return self.project_id_autocomplete_function()
-
-        if self.key in self.PROJECT_SLUG_KEYS:
-            return self.project_slug_autocomplete_function()
-
-        if self.key in self.SPAN_STATUS_KEYS:
-            return self.span_status_autocomplete_function()
-
-        return self.default_autocomplete_function()
-
-    def noop_autocomplete_function(self) -> list[TagValue]:
-        return []
+        raise NotImplementedError
 
     def project_id_autocomplete_function(self) -> list[TagValue]:
         return [
@@ -371,6 +299,50 @@ class SpanFieldValuesAutocompletionExecutor:
             if not self.query or self.query in project.slug
         ]
 
+
+class SpanFieldValuesAutocompletionExecutor(BaseSpanFieldValuesAutocompletionExecutor):
+    ID_KEYS = {
+        "id",
+        "span_id",
+        "parent_span",
+        "parent_span_id",
+        "trace",
+        "trace_id",
+        "transaction.id",
+        "transaction_id",
+        "segment.id",
+        "segment_id",
+        "profile.id",
+        "profile_id",
+        "replay.id",
+        "replay_id",
+    }
+    NUMERIC_KEYS = {"span.duration", "span.self_time"}
+    TIMESTAMP_KEYS = {"timestamp"}
+    SPAN_STATUS_KEYS = {"span.status"}
+
+    def execute(self) -> list[TagValue]:
+        if (
+            self.key in self.NUMERIC_KEYS
+            or self.key in self.ID_KEYS
+            or self.key in self.TIMESTAMP_KEYS
+        ):
+            return self.noop_autocomplete_function()
+
+        if self.key in self.PROJECT_ID_KEYS:
+            return self.project_id_autocomplete_function()
+
+        if self.key in self.PROJECT_SLUG_KEYS:
+            return self.project_slug_autocomplete_function()
+
+        if self.key in self.SPAN_STATUS_KEYS:
+            return self.span_status_autocomplete_function()
+
+        return self.default_autocomplete_function()
+
+    def noop_autocomplete_function(self) -> list[TagValue]:
+        return []
+
     def span_status_autocomplete_function(self) -> list[TagValue]:
         query = self.get_autocomplete_query_base()
 
@@ -424,3 +396,78 @@ class SpanFieldValuesAutocompletionExecutor:
             for row in results["data"]
             if row[self.key] is not None
         ]
+
+
+class EAPSpanFieldValuesAutocompletionExecutor(BaseSpanFieldValuesAutocompletionExecutor):
+    def __init__(
+        self,
+        organization: Organization,
+        snuba_params: SnubaParams,
+        key: str,
+        query: str | None,
+        max_span_tag_values: int,
+    ):
+        super().__init__(organization, snuba_params, key, query, max_span_tag_values)
+        self.attribute_key = self.resolve_attribute_key(key, snuba_params)
+
+    def resolve_attribute_key(self, key: str, snuba_params: SnubaParams) -> AttributeKey | None:
+        resolver = SearchResolver(params=snuba_params, config=SearchResolverConfig())
+        resolved, _ = resolver.resolve_attribute(key)
+        proto = resolved.proto_definition
+        if not isinstance(proto, AttributeKey):
+            return None
+        return proto
+
+    def execute(self) -> list[TagValue]:
+        if self.key in self.PROJECT_ID_KEYS:
+            return self.project_id_autocomplete_function()
+
+        if self.key in self.PROJECT_SLUG_KEYS:
+            return self.project_slug_autocomplete_function()
+
+        return self.default_autocomplete_function()
+
+    def default_autocomplete_function(self) -> list[TagValue]:
+        if self.attribute_key is None:
+            return []
+
+        start_timestamp = Timestamp()
+        start_timestamp.FromDatetime(
+            self.snuba_params.start_date.replace(hour=0, minute=0, second=0, microsecond=0)
+        )
+
+        end_timestamp = Timestamp()
+        end_timestamp.FromDatetime(
+            self.snuba_params.end_date.replace(hour=0, minute=0, second=0, microsecond=0)
+            + timedelta(days=1)
+        )
+
+        query = translate_escape_sequences(self.query)
+        rpc_request = AttributeValuesRequest(
+            meta=RequestMeta(
+                organization_id=self.organization.id,
+                cogs_category="performance",
+                referrer=Referrer.API_SPANS_TAG_VALUES_RPC.value,
+                project_ids=self.snuba_params.project_ids,
+                start_timestamp=start_timestamp,
+                end_timestamp=end_timestamp,
+                trace_item_name=TraceItemName.TRACE_ITEM_NAME_EAP_SPANS,
+            ),
+            name=self.attribute_key.name,
+            value_substring_match=query,
+            limit=self.max_span_tag_values,
+            offset=0,
+        )
+        rpc_response = snuba_rpc.rpc(rpc_request, AttributeValuesResponse)
+
+        return [
+            TagValue(
+                key=self.key,
+                value=tag_value,
+                times_seen=None,
+                first_seen=None,
+                last_seen=None,
+            )
+            for tag_value in rpc_response.values
+            if tag_value
+        ]

+ 6 - 0
src/sentry/search/eap/columns.py

@@ -197,6 +197,12 @@ SPAN_COLUMN_DEFINITIONS = {
             search_type="string",
             validator=is_event_id,
         ),
+        ResolvedColumn(
+            public_alias="transaction",
+            internal_name="sentry.segment_name",
+            search_type="string",
+            validator=is_event_id,
+        ),
         ResolvedColumn(
             public_alias="messaging.destination.name",
             internal_name="sentry.messaging.destination.name",

+ 27 - 28
src/sentry/search/eap/spans.py

@@ -257,40 +257,39 @@ class SearchResolver:
         attributes (aka. tags), but can also refer to fields like span.description"""
         if column in SPAN_COLUMN_DEFINITIONS:
             column_definition = SPAN_COLUMN_DEFINITIONS[column]
-        else:
-            # If the column isn't predefined handle it as a tag
-            tag_match = qb_constants.TYPED_TAG_KEY_RE.search(column)
-            if tag_match is None:
-                tag_match = qb_constants.TAG_KEY_RE.search(column)
-                field_type = "string"
+
+            if column in VIRTUAL_CONTEXTS:
+                column_context = VIRTUAL_CONTEXTS[column](self.params)
+            else:
+                column_context = None
+
+            if column_definition:
+                return column_definition, column_context
             else:
-                field_type = None
-            field = tag_match.group("tag") if tag_match else None
-            if field is None:
                 raise InvalidSearchQuery(f"Could not parse {column}")
-            # Assume string if a type isn't passed. eg. tags[foo]
-            if field_type is None:
-                field_type = tag_match.group("type") if tag_match else None
-
-            if field_type not in constants.TYPE_MAP:
-                raise InvalidSearchQuery(f"Unsupported type {field_type} in {column}")
-            internal_name = f"attr_str[{field}]" if field_type == "string" else f"attr_num[{field}]"
-            return (
-                ResolvedColumn(
-                    public_alias=column, internal_name=internal_name, search_type=field_type
-                ),
-                None,
-            )
 
-        if column in VIRTUAL_CONTEXTS:
-            column_context = VIRTUAL_CONTEXTS[column](self.params)
-        else:
-            column_context = None
+        if len(column) > qb_constants.MAX_TAG_KEY_LENGTH:
+            raise InvalidSearchQuery(f"{column} is too long, can be a maximum of 200 characters")
 
-        if column_definition:
-            return column_definition, column_context
+        tag_match = qb_constants.TYPED_TAG_KEY_RE.search(column)
+        if tag_match is None:
+            tag_match = qb_constants.TAG_KEY_RE.search(column)
+            field_type = "string"
         else:
+            field_type = None
+        field = tag_match.group("tag") if tag_match else column
+        if field is None:
             raise InvalidSearchQuery(f"Could not parse {column}")
+        # Assume string if a type isn't passed. eg. tags[foo]
+        if field_type is None:
+            field_type = tag_match.group("type") if tag_match else None
+
+        if field_type not in constants.TYPE_MAP:
+            raise InvalidSearchQuery(f"Unsupported type {field_type} in {column}")
+        return (
+            ResolvedColumn(public_alias=column, internal_name=field, search_type=field_type),
+            None,
+        )
 
     def resolve_aggregates(
         self, columns: list[str]

+ 3 - 3
src/sentry/search/events/builder/spans_indexed.py

@@ -70,10 +70,10 @@ class SpansEAPQueryBuilder(SpansIndexedQueryBuilderMixin, BaseQueryBuilder):
 
     def resolve_field(self, raw_field: str, alias: bool = False) -> Column:
         # try the typed regex first
-        if len(raw_field) <= 200:
-            tag_match = constants.TYPED_TAG_KEY_RE.search(raw_field)
-        else:
+        if len(raw_field) > constants.MAX_TAG_KEY_LENGTH:
             raise InvalidSearchQuery(f"{raw_field} is too long, can be a maximum of 200 characters")
+
+        tag_match = constants.TYPED_TAG_KEY_RE.search(raw_field)
         field = tag_match.group("tag") if tag_match else None
         field_type = tag_match.group("type") if tag_match else None
         if (

+ 1 - 0
src/sentry/search/events/constants.py

@@ -100,6 +100,7 @@ WEB_VITALS_PERFORMANCE_SCORE_WEIGHTS: dict[str, float] = {
     "inp": 0.30,
 }
 
+MAX_TAG_KEY_LENGTH = 200
 TAG_KEY_RE = re.compile(r"^(sentry_tags|tags)\[(?P<tag>.*)\]$")
 TYPED_TAG_KEY_RE = re.compile(r"^(sentry_tags|tags)\[(?P<tag>.*),\s*(?P<type>.*)\]$")
 # Based on general/src/protocol/tags.rs in relay

+ 45 - 66
tests/sentry/api/endpoints/test_organization_spans_fields.py

@@ -1,7 +1,6 @@
 from unittest import mock
 from uuid import uuid4
 
-import pytest
 from django.urls import reverse
 
 from sentry.testutils.cases import APITestCase, BaseSpansTestCase
@@ -548,28 +547,28 @@ class OrganizationSpansTagKeyValuesEndpointTest(BaseSpansTestCase, APITestCase):
             assert response.status_code == 200, response.data
             assert sorted(response.data, key=lambda v: v["value"]) == [
                 {
-                    "count": None,
+                    "count": mock.ANY,
                     "key": key,
                     "value": "bar",
                     "name": "bar",
-                    "firstSeen": None,
-                    "lastSeen": None,
+                    "firstSeen": mock.ANY,
+                    "lastSeen": mock.ANY,
                 },
                 {
-                    "count": None,
+                    "count": mock.ANY,
                     "key": key,
                     "value": "baz",
                     "name": "baz",
-                    "firstSeen": None,
-                    "lastSeen": None,
+                    "firstSeen": mock.ANY,
+                    "lastSeen": mock.ANY,
                 },
                 {
-                    "count": None,
+                    "count": mock.ANY,
                     "key": key,
                     "value": "foo",
                     "name": "foo",
-                    "firstSeen": None,
-                    "lastSeen": None,
+                    "firstSeen": mock.ANY,
+                    "lastSeen": mock.ANY,
                 },
             ]
 
@@ -577,20 +576,20 @@ class OrganizationSpansTagKeyValuesEndpointTest(BaseSpansTestCase, APITestCase):
             assert response.status_code == 200, response.data
             assert sorted(response.data, key=lambda v: v["value"]) == [
                 {
-                    "count": None,
+                    "count": mock.ANY,
                     "key": key,
                     "value": "bar",
                     "name": "bar",
-                    "firstSeen": None,
-                    "lastSeen": None,
+                    "firstSeen": mock.ANY,
+                    "lastSeen": mock.ANY,
                 },
                 {
-                    "count": None,
+                    "count": mock.ANY,
                     "key": key,
                     "value": "baz",
                     "name": "baz",
-                    "firstSeen": None,
-                    "lastSeen": None,
+                    "firstSeen": mock.ANY,
+                    "lastSeen": mock.ANY,
                 },
             ]
 
@@ -600,28 +599,28 @@ class OrganizationSpansTagKeyValuesEndpointTest(BaseSpansTestCase, APITestCase):
         assert response.status_code == 200, response.data
         assert sorted(response.data, key=lambda v: v["value"]) == [
             {
-                "count": None,
+                "count": mock.ANY,
                 "key": key,
                 "value": "9223372036854775100",
                 "name": "9223372036854775100",
-                "firstSeen": None,
-                "lastSeen": None,
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
             {
-                "count": None,
+                "count": mock.ANY,
                 "key": key,
                 "value": "9223372036854775299",
                 "name": "9223372036854775299",
-                "firstSeen": None,
-                "lastSeen": None,
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
             {
-                "count": None,
+                "count": mock.ANY,
                 "key": key,
                 "value": "9223372036854775399",
                 "name": "9223372036854775399",
-                "firstSeen": None,
-                "lastSeen": None,
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
         ]
 
@@ -629,20 +628,20 @@ class OrganizationSpansTagKeyValuesEndpointTest(BaseSpansTestCase, APITestCase):
         assert response.status_code == 200, response.data
         assert sorted(response.data, key=lambda v: v["value"]) == [
             {
-                "count": None,
+                "count": mock.ANY,
                 "key": key,
                 "value": "9223372036854775299",
                 "name": "9223372036854775299",
-                "firstSeen": None,
-                "lastSeen": None,
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
             {
-                "count": None,
+                "count": mock.ANY,
                 "key": key,
                 "value": "9223372036854775399",
                 "name": "9223372036854775399",
-                "firstSeen": None,
-                "lastSeen": None,
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
         ]
 
@@ -666,28 +665,28 @@ class OrganizationSpansTagKeyValuesEndpointTest(BaseSpansTestCase, APITestCase):
         assert response.status_code == 200, response.data
         assert response.data == [
             {
-                "count": 1,
+                "count": mock.ANY,
                 "key": "span.status",
                 "value": "internal_error",
                 "name": "internal_error",
-                "firstSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
-                "lastSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
             {
-                "count": 1,
+                "count": mock.ANY,
                 "key": "span.status",
                 "value": "invalid_argument",
                 "name": "invalid_argument",
-                "firstSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
-                "lastSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
             {
-                "count": 1,
+                "count": mock.ANY,
                 "key": "span.status",
                 "value": "ok",
                 "name": "ok",
-                "firstSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
-                "lastSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
         ]
 
@@ -695,20 +694,20 @@ class OrganizationSpansTagKeyValuesEndpointTest(BaseSpansTestCase, APITestCase):
         assert response.status_code == 200, response.data
         assert response.data == [
             {
-                "count": 1,
+                "count": mock.ANY,
                 "key": "span.status",
                 "value": "internal_error",
                 "name": "internal_error",
-                "firstSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
-                "lastSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
             {
-                "count": 1,
+                "count": mock.ANY,
                 "key": "span.status",
                 "value": "invalid_argument",
                 "name": "invalid_argument",
-                "firstSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
-                "lastSeen": timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
+                "firstSeen": mock.ANY,
+                "lastSeen": mock.ANY,
             },
         ]
 
@@ -737,23 +736,3 @@ class OrganizationEAPSpansTagKeyValuesEndpointTest(OrganizationSpansTagKeyValues
                 format="json",
                 **kwargs,
             )
-
-    @pytest.mark.skip("autcomplete project doesnt work yet")
-    def test_tags_keys_autocomplete_project(self):
-        super().test_tags_keys_autocomplete_project()
-
-    @pytest.mark.skip("autcomplete span.status doesnt work yet")
-    def test_tags_keys_autocomplete_span_status(self):
-        super().test_tags_keys_autocomplete_project()
-
-    @pytest.mark.skip("autcomplete transaction doesnt work yet")
-    def test_transaction_keys_autocomplete(self):
-        super().test_transaction_keys_autocomplete()
-
-    @pytest.mark.skip("autcomplete transaction doesnt work yet")
-    def test_transaction_keys_autocomplete_substring(self):
-        super().test_transaction_keys_autocomplete_substring()
-
-    @pytest.mark.skip("autcomplete transaction doesnt work yet")
-    def test_transaction_keys_autocomplete_substring_with_asterisk(self):
-        super().test_transaction_keys_autocomplete_substring_with_asterisk()

+ 3 - 3
tests/sentry/search/eap/test_spans.py

@@ -264,21 +264,21 @@ class SearchResolverColumnTest(TestCase):
     def test_simple_tag(self):
         resolved_column, virtual_context = self.resolver.resolve_column("tags[foo]")
         assert resolved_column.proto_definition == AttributeKey(
-            name="attr_str[foo]", type=AttributeKey.Type.TYPE_STRING
+            name="foo", type=AttributeKey.Type.TYPE_STRING
         )
         assert virtual_context is None
 
     def test_simple_string_tag(self):
         resolved_column, virtual_context = self.resolver.resolve_column("tags[foo, string]")
         assert resolved_column.proto_definition == AttributeKey(
-            name="attr_str[foo]", type=AttributeKey.Type.TYPE_STRING
+            name="foo", type=AttributeKey.Type.TYPE_STRING
         )
         assert virtual_context is None
 
     def test_simple_number_tag(self):
         resolved_column, virtual_context = self.resolver.resolve_column("tags[foo, number]")
         assert resolved_column.proto_definition == AttributeKey(
-            name="attr_num[foo]", type=AttributeKey.Type.TYPE_INT
+            name="foo", type=AttributeKey.Type.TYPE_INT
         )
         assert virtual_context is None