Browse Source

feat(ddm): Return correct data format when no queries are run in metrics/query (#68261)

Riccardo Busetti 11 months ago
parent
commit
1dd7f75119

+ 0 - 5
src/sentry/sentry_metrics/querying/data/api.py

@@ -39,11 +39,6 @@ def run_metrics_queries_plan(
         A MetricsQueriesPlanResult object which encapsulates the results of the plan and allows a QueryTransformer
         to be run on the data.
     """
-    # For now, if the query plan is empty, we return an empty dictionary. In the future, we might want to default
-    # to a better data type.
-    if metrics_queries_plan.is_empty():
-        return MetricsQueriesPlanResult([])
-
     # We build the basic query that contains the metadata which will be shared across all queries.
     base_query = MetricsQuery(
         start=start,

+ 4 - 6
src/sentry/sentry_metrics/querying/data/execution.py

@@ -812,13 +812,11 @@ class QueryExecutor:
         if query_type == QueryType.TOTALS_AND_SERIES:
             series_query = replace(totals_query, type=ScheduledQueryType.SERIES)
 
-        final_query = replace(totals_query, next=series_query)
-
         # We initialize the query by performing type-aware mutations that prepare the query to be executed correctly
         # (e.g., adding `totals` to a totals query...).
-        self._scheduled_queries.append(
-            final_query.initialize(
-                self._organization, self._projects, self._blocked_metrics_for_projects
-            )
+        final_query = replace(totals_query, next=series_query).initialize(
+            self._organization, self._projects, self._blocked_metrics_for_projects
         )
+
+        self._scheduled_queries.append(final_query)
         self._query_results.append(None)

+ 13 - 9
src/sentry/sentry_metrics/querying/data/transformation/metrics_api.py

@@ -237,9 +237,16 @@ class MetricsAPIQueryResultsTransformer(QueryResultsTransformer[Mapping[str, Any
         Returns:
             A mapping containing the data transformed in the correct format.
         """
-        # If we have not run any queries, we won't return anything back.
+        base_result: dict[str, Any] = {
+            "data": [],
+            "meta": [],
+            "start": None,
+            "end": None,
+            "intervals": [],
+        }
+
         if not query_results:
-            return {}
+            return base_result
 
         # We first build intermediate results that we can work efficiently with.
         queries_groups, queries_meta = self._build_intermediate_results(query_results)
@@ -277,13 +284,10 @@ class MetricsAPIQueryResultsTransformer(QueryResultsTransformer[Mapping[str, Any
         for query_meta in queries_meta:
             transformed_queries_meta.append([meta.meta for meta in query_meta])
 
-        base_result = {
-            "data": transformed_queries_groups,
-            "meta": transformed_queries_meta,
-            "start": start,
-            "end": end,
-        }
-
+        base_result["data"] = transformed_queries_groups
+        base_result["meta"] = transformed_queries_meta
+        base_result["start"] = start
+        base_result["end"] = end
         if intervals is not None:
             base_result["intervals"] = intervals
 

+ 1 - 1
tests/sentry/api/endpoints/test_organization_metrics_query.py

@@ -74,7 +74,6 @@ class OrganizationMetricsQueryTest(MetricsAPIBaseTestCase):
                 "includeSeries": "false",
             },
         )
-        assert "intervals" not in response.data
         assert response.data["data"] == [[{"by": {}, "totals": 18.0}]]
         assert response.data["meta"] == [
             [
@@ -90,6 +89,7 @@ class OrganizationMetricsQueryTest(MetricsAPIBaseTestCase):
                 },
             ]
         ]
+        assert response.data["intervals"] == []
 
     def test_query_with_disabled_org(self):
         with self.options({"custom-metrics-querying-disabled-orgs": [self.organization.id]}):

+ 22 - 2
tests/sentry/sentry_metrics/querying/data/test_api.py

@@ -133,6 +133,26 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             query_type,
         ).apply_transformer(self.query_transformer)
 
+    @with_feature("organizations:ddm-metrics-api-unit-normalization")
+    def test_query_with_no_formulas(self) -> None:
+        query_1 = self.mql("sum", TransactionMRI.DURATION.value, "transaction:/bar")
+        plan = MetricsQueriesPlan().declare_query("query_1", query_1)
+
+        results = self.run_query(
+            metrics_queries_plan=plan,
+            start=self.now() - timedelta(minutes=30),
+            end=self.now() + timedelta(hours=1, minutes=30),
+            interval=3600,
+            organization=self.project.organization,
+            projects=[self.project],
+            environments=[],
+            referrer="metrics.data.api",
+        )
+        assert len(results["data"]) == 0
+        assert len(results["meta"]) == 0
+        assert results["start"] is None
+        assert results["end"] is None
+
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     @pytest.mark.xfail
     def test_query_with_empty_results(self) -> None:
@@ -237,7 +257,7 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             referrer="metrics.data.api",
             query_type=QueryType.TOTALS,
         )
-        assert "intervals" not in results
+        assert results["intervals"] == []
         data = results["data"]
         assert len(data) == 1
         assert data[0][0]["by"] == {}
@@ -498,7 +518,7 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             referrer="metrics.data.api",
             query_type=QueryType.TOTALS,
         )
-        assert "intervals" not in results
+        assert results["intervals"] == []
         data = results["data"]
         assert len(data) == 1
         assert len(data[0]) == 2