airbnb/caravel

View on GitHub
superset/sqllab/exceptions.py

Summary

Maintainability
A
2 hrs
Test Coverage
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
from __future__ import annotations

import os
from typing import TYPE_CHECKING

from flask_babel import lazy_gettext as _

from superset.errors import SupersetError, SupersetErrorType
from superset.exceptions import SupersetException

if TYPE_CHECKING:
    from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext


class SqlLabException(SupersetException):
    sql_json_execution_context: SqlJsonExecutionContext
    failed_reason_msg: str
    suggestion_help_msg: str | None

    def __init__(  # pylint: disable=too-many-arguments
        self,
        sql_json_execution_context: SqlJsonExecutionContext,
        error_type: SupersetErrorType | None = None,
        reason_message: str | None = None,
        exception: Exception | None = None,
        suggestion_help_msg: str | None = None,
    ) -> None:
        self.sql_json_execution_context = sql_json_execution_context
        self.failed_reason_msg = self._get_reason(reason_message, exception)
        self.suggestion_help_msg = suggestion_help_msg
        if error_type is None:
            if exception is not None:
                if (
                    hasattr(exception, "error_type")
                    and exception.error_type is not None
                ):
                    error_type = exception.error_type
                elif hasattr(exception, "error") and isinstance(
                    exception.error, SupersetError
                ):
                    error_type = exception.error.error_type
            else:
                error_type = SupersetErrorType.GENERIC_BACKEND_ERROR

        super().__init__(self._generate_message(), exception, error_type)

    def _generate_message(self) -> str:
        msg = _(
            "Failed to execute %(query)s",
            query=self.sql_json_execution_context.get_query_details(),
        )
        if self.failed_reason_msg:
            msg = msg + self.failed_reason_msg
        if self.suggestion_help_msg is not None:
            msg = f"{msg} {os.linesep} {self.suggestion_help_msg}"
        return msg

    @classmethod
    def _get_reason(
        cls, reason_message: str | None = None, exception: Exception | None = None
    ) -> str:
        if reason_message is not None:
            return f": {reason_message}"
        if exception is not None:
            if hasattr(exception, "get_message"):
                return f": {exception.get_message()}"
            if hasattr(exception, "message"):
                return f": {exception.message}"
            return f": {str(exception)}"
        return ""


QUERY_IS_FORBIDDEN_TO_ACCESS_REASON_MESSAGE = "can not access the query"


class QueryIsForbiddenToAccessException(SqlLabException):
    def __init__(
        self,
        sql_json_execution_context: SqlJsonExecutionContext,
        exception: Exception | None = None,
    ) -> None:
        super().__init__(
            sql_json_execution_context,
            SupersetErrorType.QUERY_SECURITY_ACCESS_ERROR,
            QUERY_IS_FORBIDDEN_TO_ACCESS_REASON_MESSAGE,
            exception,
        )