This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new c44ee06  fix(chart-data-api): improve chart data endpoint errors 
(#10300)
c44ee06 is described below

commit c44ee06b5d633a13221ff037065765f936b51b65
Author: Ville Brofeldt <33317356+ville...@users.noreply.github.com>
AuthorDate: Tue Jul 14 12:40:00 2020 +0300

    fix(chart-data-api): improve chart data endpoint errors (#10300)
    
    * fix: improve chart data error response
    
    * Populate error_message in QueryResult
    
    * add tests
    
    * Lint + fix incorrect raise
    
    * add more tests
---
 superset-frontend/src/chart/Chart.jsx |  2 +-
 superset/charts/api.py                |  9 ++++--
 superset/common/query_context.py      |  8 ++----
 superset/connectors/sqla/models.py    |  3 ++
 tests/charts/api_tests.py             | 52 +++++++++++++++++++++++++++++++++++
 5 files changed, 64 insertions(+), 10 deletions(-)

diff --git a/superset-frontend/src/chart/Chart.jsx 
b/superset-frontend/src/chart/Chart.jsx
index 8a728b0..9562304 100644
--- a/superset-frontend/src/chart/Chart.jsx
+++ b/superset-frontend/src/chart/Chart.jsx
@@ -143,7 +143,7 @@ class Chart extends React.PureComponent {
     return (
       <ErrorMessageWithStackTrace
         error={queryResponse?.errors?.[0]}
-        message={chartAlert}
+        message={chartAlert || queryResponse?.message}
         link={queryResponse ? queryResponse.link : null}
         stackTrace={chartStackTrace}
       />
diff --git a/superset/charts/api.py b/superset/charts/api.py
index c7b0d31..af359a4 100644
--- a/superset/charts/api.py
+++ b/superset/charts/api.py
@@ -422,7 +422,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
     @protect()
     @safe
     @statsd_metrics
-    def data(self) -> Response:
+    def data(self) -> Response:  # pylint: disable=too-many-return-statements
         """
         Takes a query context constructed in the client and returns payload
         data response for the given query.
@@ -465,13 +465,16 @@ class ChartRestApi(BaseSupersetModelRestApi):
             return self.response_400(message="Request is incorrect")
         except ValidationError as error:
             return self.response_400(
-                _("Request is incorrect: %(error)s", error=error.messages)
+                message=_("Request is incorrect: %(error)s", 
error=error.messages)
             )
         try:
             query_context.raise_for_access()
         except SupersetSecurityException:
             return self.response_401()
         payload = query_context.get_payload()
+        for query in payload:
+            if query["error"]:
+                return self.response_400(message=f"Error: {query['error']}")
         result_format = query_context.result_format
         if result_format == ChartDataResultFormat.CSV:
             # return the first result
@@ -491,7 +494,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
             resp.headers["Content-Type"] = "application/json; charset=utf-8"
             return resp
 
-        raise self.response_400(message=f"Unsupported result_format: 
{result_format}")
+        return self.response_400(message=f"Unsupported result_format: 
{result_format}")
 
     @expose("/<pk>/cache_screenshot/", methods=["GET"])
     @protect()
diff --git a/superset/common/query_context.py b/superset/common/query_context.py
index bf0a3e2..e602fbf 100644
--- a/superset/common/query_context.py
+++ b/superset/common/query_context.py
@@ -111,8 +111,7 @@ class QueryContext:
                 self.df_metrics_to_num(df, query_object)
 
             df.replace([np.inf, -np.inf], np.nan)
-
-        df = query_object.exec_post_processing(df)
+            df = query_object.exec_post_processing(df)
 
         return {
             "query": result.query,
@@ -160,10 +159,7 @@ class QueryContext:
         df = payload["df"]
         status = payload["status"]
         if status != utils.QueryStatus.FAILED:
-            if df.empty:
-                payload["error"] = "No data"
-            else:
-                payload["data"] = self.get_data(df)
+            payload["data"] = self.get_data(df)
         del payload["df"]
         if self.result_type == utils.ChartDataResultType.RESULTS:
             return {"data": payload["data"]}
diff --git a/superset/connectors/sqla/models.py 
b/superset/connectors/sqla/models.py
index cc2a7b2..c73152a 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -1133,6 +1133,7 @@ class SqlaTable(  # pylint: 
disable=too-many-public-methods,too-many-instance-at
         sql = query_str_ext.sql
         status = utils.QueryStatus.SUCCESS
         errors = None
+        error_message = None
 
         def mutator(df: pd.DataFrame) -> None:
             """
@@ -1163,6 +1164,7 @@ class SqlaTable(  # pylint: 
disable=too-many-public-methods,too-many-instance-at
             )
             db_engine_spec = self.database.db_engine_spec
             errors = db_engine_spec.extract_errors(ex)
+            error_message = utils.error_msg_from_exception(ex)
 
         return QueryResult(
             status=status,
@@ -1170,6 +1172,7 @@ class SqlaTable(  # pylint: 
disable=too-many-public-methods,too-many-instance-at
             duration=datetime.now() - qry_start_dttm,
             query=sql,
             errors=errors,
+            error_message=error_message,
         )
 
     def get_sqla_table_object(self) -> Table:
diff --git a/tests/charts/api_tests.py b/tests/charts/api_tests.py
index 78461cf..03a9864 100644
--- a/tests/charts/api_tests.py
+++ b/tests/charts/api_tests.py
@@ -708,6 +708,28 @@ class TestChartApi(SupersetTestCase, 
ApiOwnersTestCaseMixin):
         result = response_payload["result"][0]
         self.assertEqual(result["rowcount"], 5)
 
+    def test_chart_data_incorrect_result_type(self):
+        """
+        Chart data API: Test chart data with unsupported result type
+        """
+        self.login(username="admin")
+        table = self.get_table_by_name("birth_names")
+        request_payload = get_query_context(table.name, table.id, table.type)
+        request_payload["result_type"] = "qwerty"
+        rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
+        self.assertEqual(rv.status_code, 400)
+
+    def test_chart_data_incorrect_result_format(self):
+        """
+        Chart data API: Test chart data with unsupported result format
+        """
+        self.login(username="admin")
+        table = self.get_table_by_name("birth_names")
+        request_payload = get_query_context(table.name, table.id, table.type)
+        request_payload["result_format"] = "qwerty"
+        rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
+        self.assertEqual(rv.status_code, 400)
+
     def test_chart_data_mixed_case_filter_op(self):
         """
         Chart data API: Ensure mixed case filter operator generates valid 
result
@@ -722,6 +744,36 @@ class TestChartApi(SupersetTestCase, 
ApiOwnersTestCaseMixin):
         result = response_payload["result"][0]
         self.assertEqual(result["rowcount"], 10)
 
+    def test_chart_data_no_data(self):
+        """
+        Chart data API: Test chart data with empty result
+        """
+        self.login(username="admin")
+        table = self.get_table_by_name("birth_names")
+        request_payload = get_query_context(table.name, table.id, table.type)
+        request_payload["queries"][0]["filters"] = [
+            {"col": "gender", "op": "==", "val": "foo"}
+        ]
+        rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
+        self.assertEqual(rv.status_code, 200)
+        response_payload = json.loads(rv.data.decode("utf-8"))
+        result = response_payload["result"][0]
+        self.assertEqual(result["rowcount"], 0)
+        self.assertEqual(result["data"], [])
+
+    def test_chart_data_incorrect_request(self):
+        """
+        Chart data API: Test chart data with invalid SQL
+        """
+        self.login(username="admin")
+        table = self.get_table_by_name("birth_names")
+        request_payload = get_query_context(table.name, table.id, table.type)
+        request_payload["queries"][0]["filters"] = []
+        # erroneus WHERE-clause
+        request_payload["queries"][0]["extras"]["where"] = "(gender abc def)"
+        rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
+        self.assertEqual(rv.status_code, 400)
+
     def test_chart_data_with_invalid_datasource(self):
         """Chart data API: Test chart data query with invalid schema
         """

Reply via email to