diff --git a/docs/src/pages/docs/Miscellaneous/issue_codes.mdx b/docs/src/pages/docs/Miscellaneous/issue_codes.mdx index 9abb6253a6ed..e239a7b0a065 100644 --- a/docs/src/pages/docs/Miscellaneous/issue_codes.mdx +++ b/docs/src/pages/docs/Miscellaneous/issue_codes.mdx @@ -271,8 +271,6 @@ One or more parameters specified in the query are malformatted. ``` The query contains one or more malformed template parameters. Please check your query and confirm that all template parameters are surround by double braces, for example, "{{ ds }}". Then, try running your query again. -The object does not exist in this database. -``` ## Issue 1029 @@ -289,3 +287,27 @@ The query potentially has a syntax error. ``` The query might have a syntax error. Please check and run again. + +## Issue 1031 + +``` +The results backend no longer has the data from the query. +``` + +The results from the query might have been deleted from the results backend after some period. Please re-run your query. + +## Issue 1032 + +``` +The query associated with the results was deleted. +``` + +The query associated with the stored results no longer exists. Please re-run your query. + +## Issue 1033 + +``` +The results stored in the backend were stored in a different format, and no longer can be deserialized. +``` + +The query results were stored in a format that is no longer supported. Please re-run your query. diff --git a/superset-frontend/src/components/ErrorMessage/types.ts b/superset-frontend/src/components/ErrorMessage/types.ts index e70d61cf85aa..fb26836b03c6 100644 --- a/superset-frontend/src/components/ErrorMessage/types.ts +++ b/superset-frontend/src/components/ErrorMessage/types.ts @@ -52,6 +52,7 @@ export const ErrorTypeEnum = { TABLE_SECURITY_ACCESS_ERROR: 'TABLE_SECURITY_ACCESS_ERROR', DATASOURCE_SECURITY_ACCESS_ERROR: 'DATASOURCE_SECURITY_ACCESS_ERROR', DATABASE_SECURITY_ACCESS_ERROR: 'DATABASE_SECURITY_ACCESS_ERROR', + QUERY_SECURITY_ACCESS_ERROR: 'QUERY_SECURITY_ACCESS_ERROR', MISSING_OWNERSHIP_ERROR: 'MISSING_OWNERSHIP_ERROR', // Other errors @@ -65,6 +66,7 @@ export const ErrorTypeEnum = { INVALID_CTAS_QUERY_ERROR: 'INVALID_CTAS_QUERY_ERROR', INVALID_CVAS_QUERY_ERROR: 'INVALID_CVAS_QUERY_ERROR', SQLLAB_TIMEOUT_ERROR: 'SQLLAB_TIMEOUT_ERROR', + RESULTS_BACKEND_ERROR: 'RESULTS_BACKEND_ERROR', // Generic errors GENERIC_COMMAND_ERROR: 'GENERIC_COMMAND_ERROR', diff --git a/superset-frontend/src/setup/setupErrorMessages.ts b/superset-frontend/src/setup/setupErrorMessages.ts index e8d8198245a6..40c8b0135ce7 100644 --- a/superset-frontend/src/setup/setupErrorMessages.ts +++ b/superset-frontend/src/setup/setupErrorMessages.ts @@ -71,10 +71,18 @@ export default function setupErrorMessages() { ErrorTypeEnum.INVALID_CVAS_QUERY_ERROR, DatabaseErrorMessage, ); + errorMessageComponentRegistry.registerValue( + ErrorTypeEnum.QUERY_SECURITY_ACCESS_ERROR, + DatabaseErrorMessage, + ); errorMessageComponentRegistry.registerValue( ErrorTypeEnum.CONNECTION_INVALID_HOSTNAME_ERROR, DatabaseErrorMessage, ); + errorMessageComponentRegistry.registerValue( + ErrorTypeEnum.RESULTS_BACKEND_ERROR, + DatabaseErrorMessage, + ); errorMessageComponentRegistry.registerValue( ErrorTypeEnum.SQLLAB_TIMEOUT_ERROR, DatabaseErrorMessage, diff --git a/superset/errors.py b/superset/errors.py index 63cd94130774..d2845aa5b1ad 100644 --- a/superset/errors.py +++ b/superset/errors.py @@ -61,6 +61,7 @@ class SupersetErrorType(str, Enum): TABLE_SECURITY_ACCESS_ERROR = "TABLE_SECURITY_ACCESS_ERROR" DATASOURCE_SECURITY_ACCESS_ERROR = "DATASOURCE_SECURITY_ACCESS_ERROR" DATABASE_SECURITY_ACCESS_ERROR = "DATABASE_SECURITY_ACCESS_ERROR" + QUERY_SECURITY_ACCESS_ERROR = "QUERY_SECURITY_ACCESS_ERROR" MISSING_OWNERSHIP_ERROR = "MISSING_OWNERSHIP_ERROR" # Other errors @@ -74,6 +75,7 @@ class SupersetErrorType(str, Enum): INVALID_CTAS_QUERY_ERROR = "INVALID_CTAS_QUERY_ERROR" INVALID_CVAS_QUERY_ERROR = "INVALID_CVAS_QUERY_ERROR" SQLLAB_TIMEOUT_ERROR = "SQLLAB_TIMEOUT_ERROR" + RESULTS_BACKEND_ERROR = "RESULTS_BACKEND_ERROR" # Generic errors GENERIC_COMMAND_ERROR = "GENERIC_COMMAND_ERROR" @@ -331,7 +333,29 @@ class SupersetErrorType(str, Enum): }, ], SupersetErrorType.SYNTAX_ERROR: [ - {"code": 1030, "message": _("Issue 1029 - The query has a syntax error."),}, + {"code": 1030, "message": _("Issue 1030 - The query has a syntax error.")}, + ], + SupersetErrorType.RESULTS_BACKEND_ERROR: [ + { + "code": 1031, + "message": _( + "Issue 1031 - The results backend no longer has the data from the " + "query." + ), + }, + { + "code": 1032, + "message": _( + "Issue 1032 - The query associated with the results was deleted." + ), + }, + { + "code": 1033, + "message": _( + "Issue 1033 - The results stored in the backend were stored in a " + "different format, and no longer can be deserialized." + ), + }, ], } diff --git a/superset/exceptions.py b/superset/exceptions.py index f22f359a3eb7..f9587f3198f0 100644 --- a/superset/exceptions.py +++ b/superset/exceptions.py @@ -43,9 +43,11 @@ def exception(self) -> Optional[Exception]: class SupersetErrorException(SupersetException): """Exceptions with a single SupersetErrorType associated with them""" - def __init__(self, error: SupersetError) -> None: + def __init__(self, error: SupersetError, status: Optional[int] = None) -> None: super().__init__(error.message) self.error = error + if status is not None: + self.status = status class SupersetGenericErrorException(SupersetErrorException): @@ -81,9 +83,13 @@ def __init__( class SupersetErrorsException(SupersetException): """Exceptions with multiple SupersetErrorType associated with them""" - def __init__(self, errors: List[SupersetError]) -> None: + def __init__( + self, errors: List[SupersetError], status: Optional[int] = None + ) -> None: super().__init__(str(errors)) self.errors = errors + if status is not None: + self.status = status class SupersetTimeoutException(SupersetErrorFromParamsException): diff --git a/superset/views/core.py b/superset/views/core.py index 3be231475c20..b800728dc5d7 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2196,39 +2196,81 @@ def results_exec( # pylint: disable=too-many-return-statements now_as_float() - read_from_results_backend_start, ) if not blob: - return json_error_response( - "Data could not be retrieved. " "You may want to re-run the query.", + raise SupersetErrorException( + SupersetError( + message=__( + "Data could not be retrieved from the results backend. You " + "need to re-run the original query." + ), + error_type=SupersetErrorType.RESULTS_BACKEND_ERROR, + level=ErrorLevel.ERROR, + ), status=410, ) query = db.session.query(Query).filter_by(results_key=key).one_or_none() if query is None: - return json_error_response( - "Data could not be retrieved. You may want to re-run the query.", + raise SupersetErrorException( + SupersetError( + message=__( + "The query associated with these results could not be find. " + "You need to re-run the original query." + ), + error_type=SupersetErrorType.RESULTS_BACKEND_ERROR, + level=ErrorLevel.ERROR, + ), status=404, ) try: query.raise_for_access() except SupersetSecurityException as ex: - return json_errors_response([ex.error], status=403) + raise SupersetErrorException( + SupersetError( + message=__( + "You are not authorized to see this query. If you think this " + "is an error, please reach out to your administrator." + ), + error_type=SupersetErrorType.QUERY_SECURITY_ACCESS_ERROR, + level=ErrorLevel.ERROR, + ), + status=403, + ) from ex payload = utils.zlib_decompress(blob, decode=not results_backend_use_msgpack) try: obj = _deserialize_results_payload( payload, query, cast(bool, results_backend_use_msgpack) ) - except SerializationError: - return json_error_response( - __("Data could not be deserialized. You may want to re-run the query."), + except SerializationError as ex: + raise SupersetErrorException( + SupersetError( + message=__( + "Data could not be deserialized from the results backend. The " + "storage format might have changed, rendering the old data " + "stake. You need to re-run the original query." + ), + error_type=SupersetErrorType.RESULTS_BACKEND_ERROR, + level=ErrorLevel.ERROR, + ), status=404, - ) + ) from ex if "rows" in request.args: try: rows = int(request.args["rows"]) - except ValueError: - return json_error_response("Invalid `rows` argument", status=400) + except ValueError as ex: + raise SupersetErrorException( + SupersetError( + message=__( + "The provided `rows` argument is not a valid integer." + ), + error_type=SupersetErrorType.INVALID_PAYLOAD_SCHEMA_ERROR, + level=ErrorLevel.ERROR, + ), + status=400, + ) from ex + obj = apply_display_max_row_limit(obj, rows) return json_success(