Skip to content

Commit

Permalink
feat: more SIP-40 errors (apache#15482)
Browse files Browse the repository at this point in the history
  • Loading branch information
betodealmeida authored and cccs-RyanS committed Dec 17, 2021
1 parent 6f6f857 commit c02d4f8
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 14 deletions.
16 changes: 16 additions & 0 deletions docs/src/pages/docs/Miscellaneous/issue_codes.mdx
Expand Up @@ -311,3 +311,19 @@ The results stored in the backend were stored in a different format, and no long
```

The query results were stored in a format that is no longer supported. Please re-run your query.

## Issue 1035

```
Failed to start remote query on a worker.
```

The query was not started by an asynchronous worker. Please reach out to your administrator for further assistance.

## Issue 1036

```
The database was deleted.
```

The operation failed because the database referenced no longer exists. Please reach out to your administrator for further assistance.
2 changes: 2 additions & 0 deletions superset-frontend/src/components/ErrorMessage/types.ts
Expand Up @@ -57,6 +57,7 @@ export const ErrorTypeEnum = {

// Other errors
BACKEND_TIMEOUT_ERROR: 'BACKEND_TIMEOUT_ERROR',
DATABASE_NOT_FOUND_ERROR: 'DATABASE_NOT_FOUND_ERROR',

// Sqllab error
MISSING_TEMPLATE_PARAMS_ERROR: 'MISSING_TEMPLATE_PARAMS_ERROR',
Expand All @@ -67,6 +68,7 @@ export const ErrorTypeEnum = {
INVALID_CVAS_QUERY_ERROR: 'INVALID_CVAS_QUERY_ERROR',
SQLLAB_TIMEOUT_ERROR: 'SQLLAB_TIMEOUT_ERROR',
RESULTS_BACKEND_ERROR: 'RESULTS_BACKEND_ERROR',
ASYNC_WORKERS_ERROR: 'ASYNC_WORKERS_ERROR',

// Generic errors
GENERIC_COMMAND_ERROR: 'GENERIC_COMMAND_ERROR',
Expand Down
8 changes: 8 additions & 0 deletions superset-frontend/src/setup/setupErrorMessages.ts
Expand Up @@ -35,6 +35,10 @@ export default function setupErrorMessages() {
ErrorTypeEnum.BACKEND_TIMEOUT_ERROR,
TimeoutErrorMessage,
);
errorMessageComponentRegistry.registerValue(
ErrorTypeEnum.DATABASE_NOT_FOUND_ERROR,
DatabaseErrorMessage,
);
errorMessageComponentRegistry.registerValue(
ErrorTypeEnum.GENERIC_DB_ENGINE_ERROR,
DatabaseErrorMessage,
Expand Down Expand Up @@ -83,6 +87,10 @@ export default function setupErrorMessages() {
ErrorTypeEnum.RESULTS_BACKEND_ERROR,
DatabaseErrorMessage,
);
errorMessageComponentRegistry.registerValue(
ErrorTypeEnum.ASYNC_WORKERS_ERROR,
DatabaseErrorMessage,
);
errorMessageComponentRegistry.registerValue(
ErrorTypeEnum.SQLLAB_TIMEOUT_ERROR,
DatabaseErrorMessage,
Expand Down
6 changes: 6 additions & 0 deletions superset/errors.py
Expand Up @@ -66,6 +66,7 @@ class SupersetErrorType(str, Enum):

# Other errors
BACKEND_TIMEOUT_ERROR = "BACKEND_TIMEOUT_ERROR"
DATABASE_NOT_FOUND_ERROR = "DATABASE_NOT_FOUND_ERROR"

# Sql Lab errors
MISSING_TEMPLATE_PARAMS_ERROR = "MISSING_TEMPLATE_PARAMS_ERROR"
Expand All @@ -76,6 +77,7 @@ class SupersetErrorType(str, Enum):
INVALID_CVAS_QUERY_ERROR = "INVALID_CVAS_QUERY_ERROR"
SQLLAB_TIMEOUT_ERROR = "SQLLAB_TIMEOUT_ERROR"
RESULTS_BACKEND_ERROR = "RESULTS_BACKEND_ERROR"
ASYNC_WORKERS_ERROR = "ASYNC_WORKERS_ERROR"

# Generic errors
GENERIC_COMMAND_ERROR = "GENERIC_COMMAND_ERROR"
Expand Down Expand Up @@ -131,6 +133,8 @@ class SupersetErrorType(str, Enum):
"The results stored in the backend were stored in a "
"different format, and no longer can be deserialized."
),
1035: _("Failed to start remote query on a worker."),
1036: _("The database was deleted."),
}


Expand Down Expand Up @@ -163,6 +167,8 @@ class SupersetErrorType(str, Enum):
SupersetErrorType.OBJECT_DOES_NOT_EXIST_ERROR: [1029],
SupersetErrorType.SYNTAX_ERROR: [1030],
SupersetErrorType.RESULTS_BACKEND_ERROR: [1031, 1032, 1033],
SupersetErrorType.ASYNC_WORKERS_ERROR: [1035],
SupersetErrorType.DATABASE_NOT_FOUND_ERROR: [1011, 1036],
}


Expand Down
4 changes: 3 additions & 1 deletion superset/exceptions.py
Expand Up @@ -53,14 +53,16 @@ def __init__(self, error: SupersetError, status: Optional[int] = None) -> None:
class SupersetGenericErrorException(SupersetErrorException):
"""Exceptions that are too generic to have their own type"""

def __init__(self, message: str) -> None:
def __init__(self, message: str, status: Optional[int] = None) -> None:
super().__init__(
SupersetError(
message=message,
error_type=SupersetErrorType.GENERIC_BACKEND_ERROR,
level=ErrorLevel.ERROR,
)
)
if status is not None:
self.status = status


class SupersetErrorFromParamsException(SupersetErrorException):
Expand Down
74 changes: 62 additions & 12 deletions superset/views/core.py
Expand Up @@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
# pylint: disable=comparison-with-callable, line-too-long, too-many-branches
import dataclasses
import logging
import re
from contextlib import closing
Expand Down Expand Up @@ -2035,10 +2036,21 @@ def sqllab_viz(self) -> FlaskResponse: # pylint: disable=no-self-use
table_name = data["datasourceName"]
database_id = data["dbId"]
except KeyError:
return json_error_response("Missing required fields", status=400)
raise SupersetGenericErrorException(
"One or more required fields are missing in the request. Please try "
"again, and if the problem persists conctact your administrator.",
status=400,
)
database = db.session.query(Database).get(database_id)
if not database:
return json_error_response("Database not found", status=400)
raise SupersetErrorException(
SupersetError(
message="The database was not found.",
error_type=SupersetErrorType.DATABASE_NOT_FOUND_ERROR,
level=ErrorLevel.ERROR,
),
status=404,
)
table = (
db.session.query(SqlaTable)
.filter_by(database_id=database_id, table_name=table_name)
Expand Down Expand Up @@ -2101,7 +2113,14 @@ def select_star(
stats_logger.incr(
f"deprecated.{self.__class__.__name__}.select_star.database_not_found"
)
return json_error_response("Not found", 404)
raise SupersetErrorException(
SupersetError(
message="The database was not found.",
error_type=SupersetErrorType.DATABASE_NOT_FOUND_ERROR,
level=ErrorLevel.ERROR,
),
status=404,
)
schema = utils.parse_js_uri_path_item(schema, eval_undefined=True)
table_name = utils.parse_js_uri_path_item(table_name) # type: ignore
if not self.appbuilder.sm.can_access_table(database, Table(table_name, schema)):
Expand All @@ -2114,7 +2133,18 @@ def select_star(
table_name,
schema,
)
return json_error_response("Not found", 404)
raise SupersetErrorException(
SupersetError(
message=__(
"You are not authorized to fetch samples from this table. 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,
)
stats_logger.incr(f"deprecated.{self.__class__.__name__}.select_star.success")
return json_success(
database.select_star(
Expand Down Expand Up @@ -2425,15 +2455,21 @@ def _sql_json_async( # pylint: disable=too-many-arguments
)
except Exception as ex: # pylint: disable=broad-except
logger.exception("Query %i: %s", query.id, str(ex))
msg = _(
"Failed to start remote query on a worker. "
"Tell your administrator to verify the availability of "
"the message queue."

message = _("Failed to start remote query on a worker.")
error = SupersetError(
message=message,
error_type=SupersetErrorType.ASYNC_WORKERS_ERROR,
level=ErrorLevel.ERROR,
)
error_payload = dataclasses.asdict(error)

query.set_extra_json_key("errors", [error_payload])
query.status = QueryStatus.FAILED
query.error_message = msg
query.error_message = message
session.commit()
return json_error_response("{}".format(msg))

raise SupersetErrorException(error)

# Update saved query with execution info from the query execution
QueryDAO.update_saved_query_exec_info(query_id)
Expand Down Expand Up @@ -2638,9 +2674,23 @@ def sql_json_exec( # pylint: disable=too-many-statements,too-many-locals
try:
query.raise_for_access()
except SupersetSecurityException as ex:
message = _(
"You are not authorized to see this query. If you think this "
"is an error, please reach out to your administrator."
)
error = SupersetError(
message=message,
error_type=SupersetErrorType.QUERY_SECURITY_ACCESS_ERROR,
level=ErrorLevel.ERROR,
)
error_payload = dataclasses.asdict(error)

query.set_extra_json_key("errors", [error_payload])
query.status = QueryStatus.FAILED
query.error_message = message
session.commit()
return json_errors_response([ex.error], status=403)

raise SupersetErrorException(error, status=403) from ex

try:
template_processor = get_template_processor(
Expand All @@ -2657,7 +2707,7 @@ def sql_json_exec( # pylint: disable=too-many-statements,too-many-locals
'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.'
),
error=SupersetErrorType.INVALID_TEMPLATE_PARAMS_ERROR,
)
) from ex

if is_feature_enabled("ENABLE_TEMPLATE_PROCESSING"):
# pylint: disable=protected-access
Expand Down
2 changes: 1 addition & 1 deletion tests/core_tests.py
Expand Up @@ -1197,7 +1197,7 @@ def test_get_select_star_not_allowed(self):
self.login(username="gamma")
example_db = utils.get_example_database()
resp = self.client.get(f"/superset/select_star/{example_db.id}/birth_names")
self.assertEqual(resp.status_code, 404)
self.assertEqual(resp.status_code, 403)

@mock.patch("superset.views.core.results_backend_use_msgpack", False)
@mock.patch("superset.views.core.results_backend")
Expand Down

0 comments on commit c02d4f8

Please sign in to comment.