From 95b4c7b7fea8ad7c88016d7b332ad68e80ecb0e2 Mon Sep 17 00:00:00 2001 From: Antonio Rivero Martinez <38889534+Antonio-RiveroMartnez@users.noreply.github.com> Date: Thu, 10 Nov 2022 00:56:08 -0300 Subject: [PATCH] chore(bigquery): Add extra logging for BigQuery exceptions so we can have better insight on exceptions (#22024) --- superset/db_engine_specs/base.py | 11 ++++- superset/db_engine_specs/bigquery.py | 9 ++++ .../db_engine_specs/test_bigquery.py | 41 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py index 2a1363e0b..fb1430ba5 100644 --- a/superset/db_engine_specs/base.py +++ b/superset/db_engine_specs/base.py @@ -430,6 +430,15 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods """ return {} + @classmethod + def parse_error_exception(cls, exception: Exception) -> Exception: + """ + Each engine can implement and converge its own specific parser method + + :return: An Exception with a parsed string off the original exception + """ + return exception + @classmethod def get_dbapi_mapped_exception(cls, exception: Exception) -> Exception: """ @@ -443,7 +452,7 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods """ new_exception = cls.get_dbapi_exception_mapping().get(type(exception)) if not new_exception: - return exception + return cls.parse_error_exception(exception) return new_exception(str(exception)) @classmethod diff --git a/superset/db_engine_specs/bigquery.py b/superset/db_engine_specs/bigquery.py index ffeff31b1..373fc2f74 100644 --- a/superset/db_engine_specs/bigquery.py +++ b/superset/db_engine_specs/bigquery.py @@ -578,3 +578,12 @@ class BigQueryEngineSpec(BaseEngineSpec): "author__name" and "author__email", respectively. """ return [column(c["name"]).label(c["name"].replace(".", "__")) for c in cols] + + @classmethod + def parse_error_exception(cls, exception: Exception) -> Exception: + try: + return Exception(str(exception).splitlines()[0].rsplit(":")[1].strip()) + except Exception: # pylint: disable=broad-except + # If for some reason we get an exception, for example, no new line + # We will return the original exception + return exception diff --git a/tests/unit_tests/db_engine_specs/test_bigquery.py b/tests/unit_tests/db_engine_specs/test_bigquery.py index 8c33489fa..4a5c74153 100644 --- a/tests/unit_tests/db_engine_specs/test_bigquery.py +++ b/tests/unit_tests/db_engine_specs/test_bigquery.py @@ -244,3 +244,44 @@ def test_mask_encrypted_extra_when_empty() -> None: from superset.db_engine_specs.bigquery import BigQueryEngineSpec assert BigQueryEngineSpec.mask_encrypted_extra(None) is None + + +def test_parse_error_message() -> None: + """ + Test that we parse a received message and just extract the useful information. + + Example errors: + bigquery error: 400 Table \"case_detail_all_suites\" must be qualified with a dataset (e.g. dataset.table). + + (job ID: ddf30b05-44e8-4fbf-aa29-40bfccaed886) + -----Query Job SQL Follows----- + | . | . | . |\n 1:select * from case_detail_all_suites\n 2:LIMIT 1001\n | . | . | . | + """ + from superset.db_engine_specs.bigquery import BigQueryEngineSpec + + message = 'bigquery error: 400 Table "case_detail_all_suites" must be qualified with a dataset (e.g. dataset.table).\n\n(job ID: ddf30b05-44e8-4fbf-aa29-40bfccaed886)\n\n -----Query Job SQL Follows----- \n\n | . | . | . |\n 1:select * from case_detail_all_suites\n 2:LIMIT 1001\n | . | . | . |' + expected_result = '400 Table "case_detail_all_suites" must be qualified with a dataset (e.g. dataset.table).' + assert ( + str(BigQueryEngineSpec.parse_error_exception(Exception(message))) + == expected_result + ) + + +def test_parse_error_raises_exception() -> None: + """ + Test that we handle any exception we might get from calling the parse_error_exception method. + + Example errors: + 400 Syntax error: Expected "(" or keyword UNNEST but got "@" at [4:80] + bigquery error: 400 Table \"case_detail_all_suites\" must be qualified with a dataset (e.g. dataset.table). + """ + from superset.db_engine_specs.bigquery import BigQueryEngineSpec + + message = 'bigquery error: 400 Table "case_detail_all_suites" must be qualified with a dataset (e.g. dataset.table).' + message_2 = "6" + expected_result = '400 Table "case_detail_all_suites" must be qualified with a dataset (e.g. dataset.table).' + assert ( + str(BigQueryEngineSpec.parse_error_exception(Exception(message))) + == expected_result + ) + assert str(BigQueryEngineSpec.parse_error_exception(Exception(message_2))) == "6"