# 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 flask_babel import lazy_gettext as _ from marshmallow.validate import ValidationError from superset.commands.exceptions import ( CommandException, CommandInvalidError, CreateFailedError, DeleteFailedError, ImportFailedError, UpdateFailedError, ) from superset.exceptions import SupersetErrorException, SupersetErrorsException class DatabaseInvalidError(CommandInvalidError): message = _("Database parameters are invalid.") class DatabaseExistsValidationError(ValidationError): """ Marshmallow validation error for dataset already exists """ def __init__(self) -> None: super().__init__( _("A database with the same name already exists."), field_name="database_name", ) class DatabaseRequiredFieldValidationError(ValidationError): def __init__(self, field_name: str) -> None: super().__init__( [_("Field is required")], field_name=field_name, ) class DatabaseExtraJSONValidationError(ValidationError): """ Marshmallow validation error for database encrypted extra must be a valid JSON """ def __init__(self, json_error: str = "") -> None: super().__init__( [ _( "Field cannot be decoded by JSON. %(json_error)s", json_error=json_error, ) ], field_name="extra", ) class DatabaseExtraValidationError(ValidationError): """ Marshmallow validation error for database encrypted extra must be a valid JSON """ def __init__(self, key: str = "") -> None: super().__init__( [ _( "The metadata_params in Extra field " "is not configured correctly. The key " "%{key}s is invalid.", key=key, ) ], field_name="extra", ) class DatabaseConnectionSyncPermissionsError(CommandException): status = 500 message = _("Unable to sync permissions for this database connection.") class DatabaseNotFoundError(CommandException): status = 404 message = _("Database not found.") class UserNotFoundInSessionError(CommandException): status = 500 message = _("Could not validate the user in the current session.") class DatabaseSchemaUploadNotAllowed(CommandException): status = 403 message = _("Database schema is not allowed for csv uploads.") class DatabaseUploadNotSupported(CommandException): status = 422 message = _("Database type does not support file uploads.") class DatabaseUploadFailed(CommandException): status = 422 message = _("Database upload file failed") class DatabaseUploadFileTooLarge(CommandException): status = 413 message = _("Database upload file exceeds the maximum allowed size.") class DatabaseUploadSoftDeletedDatasetExistsError(DatabaseUploadFailed): """The upload targets a table whose dataset sits soft-deleted in the trash. Creating a new dataset over the same physical table would make an active twin of the hidden row and permanently block its restore, so the upload is refused before any file data is written. The message names only executable recoveries (restore, or a different table name) — there is no hard-delete/purge API surface until the purge work lands. """ def __init__(self, dataset_uuid: str) -> None: super().__init__( _( "A soft-deleted dataset (uuid %(uuid)s) already references " "this table. Restore it via POST " "/api/v1/dataset/%(uuid)s/restore before uploading, or upload " "to a different table name.", uuid=dataset_uuid, ) ) class DatabaseUploadSaveMetadataFailed(CommandException): status = 500 message = _("Database upload file failed, while saving metadata") class DatabaseCreateFailedError(CreateFailedError): message = _("Database could not be created.") class DatabaseUpdateFailedError(UpdateFailedError): message = _("Database could not be updated.") class DatabaseConnectionFailedError( # pylint: disable=too-many-ancestors DatabaseCreateFailedError, DatabaseUpdateFailedError, ): message = _("Connection failed, please check your connection settings") class MissingOAuth2TokenError(DatabaseUpdateFailedError): """ Exception for when the connection is missing an OAuth2 token and it's not possible to initiate an OAuth2 dance. """ message = _("Missing OAuth2 token") class DatabaseDeleteDatasetsExistFailedError(DeleteFailedError): message = _("Cannot delete a database that has datasets attached") class DatabaseDeleteSoftDeletedDatasetsExistFailedError( DatabaseDeleteDatasetsExistFailedError ): # Subclasses the live-datasets error so the existing API handler catches # both; only the message differs, telling the operator that the blockers # are hidden (soft-deleted) rows even though their dataset list looks empty. message = _( "Cannot delete a database whose only remaining datasets are " "soft-deleted. Restore them (POST /api/v1/dataset//restore) " "and delete them permanently once a purge capability ships, or " "remove the underlying rows out-of-band, before deleting the " "database." ) class DatabaseDeleteFailedError(DeleteFailedError): message = _("Database could not be deleted.") class DatabaseDeleteFailedReportsExistError(DatabaseDeleteFailedError): message = _("There are associated alerts or reports") class DatabaseTestConnectionFailedError(SupersetErrorsException): status = 422 message = _("Connection failed, please check your connection settings") class DatabaseSecurityUnsafeError(CommandInvalidError): message = _("Stopped an unsafe database connection") class DatabaseTestConnectionDriverError(CommandInvalidError): message = _("Could not load database driver") class DatabaseTestConnectionUnexpectedError(SupersetErrorsException): status = 422 message = _("Unexpected error occurred, please check your logs for details") class DatabaseTablesUnexpectedError(CommandException): status = 422 message = _("Unexpected error occurred, please check your logs for details") class NoValidatorConfigFoundError(SupersetErrorException): status = 422 message = _("no SQL validator is configured") class NoValidatorFoundError(SupersetErrorException): status = 422 message = _("No validator found (configured for the engine)") class ValidatorSQLError(SupersetErrorException): status = 422 message = _("Was unable to check your query") class ValidatorSQLUnexpectedError(CommandException): status = 422 message = _("An unexpected error occurred") class ValidatorSQL400Error(SupersetErrorException): status = 400 message = _("Was unable to check your query") class DatabaseImportError(ImportFailedError): message = _("Import database failed for an unknown reason") class InvalidEngineError(SupersetErrorException): status = 422 class DatabaseOfflineError(SupersetErrorException): status = 422 class InvalidParametersError(SupersetErrorsException): status = 422 class DatasetValidationError(CommandException): status = 422 def __init__(self, err: Exception) -> None: super().__init__( _( "Dataset schema is invalid, caused by: %(error)s", error=str(err), ) )