Browse Source

feat(mep): Implement get_snql_query (#35610)

- Wasn't implemented before since most visibility queries would have >1
  table. Added now since alert queries will only have 1 table
- Still throws notImplemented if theres more than 1 table
William Mak 2 years ago
parent
commit
a2a17cf8f7
2 changed files with 68 additions and 4 deletions
  1. 37 4
      src/sentry/search/events/builder.py
  2. 31 0
      tests/sentry/search/events/test_builder.py

+ 37 - 4
src/sentry/search/events/builder.py

@@ -1827,10 +1827,43 @@ class MetricsQueryBuilder(QueryBuilder):
 
         return Condition(lhs, Op(search_filter.operator), value)
 
-    def get_snql_query(self) -> List[Query]:
-        """Because metrics table queries need to make multiple requests per metric type this function cannot be
-        inmplemented see run_query"""
-        raise NotImplementedError("get_snql_query cannot be implemented for MetricsQueryBuilder")
+    def get_snql_query(self) -> Request:
+        self.validate_having_clause()
+        self.validate_orderby_clause()
+        # Need to split orderby between the 3 possible tables
+        primary, query_framework = self._create_query_framework()
+        primary_framework = query_framework.pop(primary)
+        if len(primary_framework.functions) == 0:
+            raise IncompatibleMetricsQuery("Need at least one function")
+        for query_details in query_framework.values():
+            if len(query_details.functions) > 0:
+                # More than 1 dataset means multiple queries so we can't return them here
+                raise NotImplementedError(
+                    "get_snql_query cannot be implemented for MetricsQueryBuilder"
+                )
+
+        return Request(
+            dataset=self.dataset.value,
+            app_id="default",
+            query=Query(
+                match=primary_framework.entity,
+                select=[
+                    column
+                    for column in self.columns
+                    if not isinstance(column, CurriedFunction)
+                    or column in primary_framework.functions
+                ],
+                array_join=self.array_join,
+                where=self.where,
+                having=primary_framework.having,
+                groupby=self.groupby,
+                orderby=primary_framework.orderby,
+                limit=self.limit,
+                offset=self.offset,
+                limitby=self.limitby,
+            ),
+            flags=Flags(turbo=self.turbo),
+        )
 
     def _create_query_framework(self) -> Tuple[str, Dict[str, QueryFramework]]:
         query_framework: Dict[str, QueryFramework] = {

+ 31 - 0
tests/sentry/search/events/test_builder.py

@@ -1187,6 +1187,37 @@ class MetricQueryBuilderTest(MetricBuilderBaseTest):
             get_granularity(start, end) == 60
         ), "12h at boundary, but 15 min after the boundary for start"
 
+    def test_get_snql_query(self):
+        query = MetricsQueryBuilder(self.params, "", selected_columns=["p90(transaction.duration)"])
+        snql_request = query.get_snql_query()
+        assert snql_request.dataset == "metrics"
+        snql_query = snql_request.query
+        self.assertCountEqual(
+            snql_query.select,
+            [
+                _metric_percentile_definition(self.organization.id, "90"),
+            ],
+        )
+        self.assertCountEqual(
+            query.where,
+            [
+                *self.default_conditions,
+                *_metric_conditions(self.organization.id, ["transaction.duration"]),
+            ],
+        )
+
+    def test_get_snql_query_errors_with_multiple_dataset(self):
+        query = MetricsQueryBuilder(
+            self.params, "", selected_columns=["p90(transaction.duration)", "count_unique(user)"]
+        )
+        with self.assertRaises(NotImplementedError):
+            query.get_snql_query()
+
+    def test_get_snql_query_errors_with_no_functions(self):
+        query = MetricsQueryBuilder(self.params, "", selected_columns=["project"])
+        with self.assertRaises(IncompatibleMetricsQuery):
+            query.get_snql_query()
+
     def test_run_query(self):
         self.store_metric(
             100,