mirror of
https://github.com/apache/superset.git
synced 2026-07-13 10:15:58 +00:00
Compare commits
2 Commits
supersetbo
...
db-upload-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b51ae8bdc | ||
|
|
22b3cc0480 |
@@ -111,7 +111,7 @@ athena = ["pyathena[pandas]>=2, <3"]
|
|||||||
aurora-data-api = ["preset-sqlalchemy-aurora-data-api>=0.2.8,<0.3"]
|
aurora-data-api = ["preset-sqlalchemy-aurora-data-api>=0.2.8,<0.3"]
|
||||||
bigquery = [
|
bigquery = [
|
||||||
"pandas-gbq>=0.19.1",
|
"pandas-gbq>=0.19.1",
|
||||||
"sqlalchemy-bigquery>=1.6.1",
|
"sqlalchemy-bigquery>=1.15.0",
|
||||||
"google-cloud-bigquery>=3.10.0",
|
"google-cloud-bigquery>=3.10.0",
|
||||||
]
|
]
|
||||||
clickhouse = ["clickhouse-connect>=0.5.14, <1.0"]
|
clickhouse = ["clickhouse-connect>=0.5.14, <1.0"]
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ apispec==6.6.1
|
|||||||
apsw==3.50.1.0
|
apsw==3.50.1.0
|
||||||
# via shillelagh
|
# via shillelagh
|
||||||
async-timeout==4.0.3
|
async-timeout==4.0.3
|
||||||
# via
|
# via -r requirements/base.in
|
||||||
# -r requirements/base.in
|
|
||||||
# redis
|
|
||||||
attrs==25.3.0
|
attrs==25.3.0
|
||||||
# via
|
# via
|
||||||
# cattrs
|
# cattrs
|
||||||
@@ -99,11 +97,6 @@ email-validator==2.2.0
|
|||||||
# via flask-appbuilder
|
# via flask-appbuilder
|
||||||
et-xmlfile==2.0.0
|
et-xmlfile==2.0.0
|
||||||
# via openpyxl
|
# via openpyxl
|
||||||
exceptiongroup==1.3.0
|
|
||||||
# via
|
|
||||||
# cattrs
|
|
||||||
# trio
|
|
||||||
# trio-websocket
|
|
||||||
flask==2.3.3
|
flask==2.3.3
|
||||||
# via
|
# via
|
||||||
# apache-superset (pyproject.toml)
|
# apache-superset (pyproject.toml)
|
||||||
@@ -161,7 +154,6 @@ greenlet==3.1.1
|
|||||||
# via
|
# via
|
||||||
# apache-superset (pyproject.toml)
|
# apache-superset (pyproject.toml)
|
||||||
# shillelagh
|
# shillelagh
|
||||||
# sqlalchemy
|
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
||||||
# via apache-superset (pyproject.toml)
|
# via apache-superset (pyproject.toml)
|
||||||
h11==0.16.0
|
h11==0.16.0
|
||||||
@@ -318,7 +310,7 @@ python-dateutil==2.9.0.post0
|
|||||||
# holidays
|
# holidays
|
||||||
# pandas
|
# pandas
|
||||||
# shillelagh
|
# shillelagh
|
||||||
python-dotenv==1.1.1
|
python-dotenv==1.1.0
|
||||||
# via apache-superset (pyproject.toml)
|
# via apache-superset (pyproject.toml)
|
||||||
python-geohash==0.8.5
|
python-geohash==0.8.5
|
||||||
# via apache-superset (pyproject.toml)
|
# via apache-superset (pyproject.toml)
|
||||||
@@ -403,11 +395,9 @@ typing-extensions==4.14.0
|
|||||||
# apache-superset (pyproject.toml)
|
# apache-superset (pyproject.toml)
|
||||||
# alembic
|
# alembic
|
||||||
# cattrs
|
# cattrs
|
||||||
# exceptiongroup
|
|
||||||
# limits
|
# limits
|
||||||
# pyopenssl
|
# pyopenssl
|
||||||
# referencing
|
# referencing
|
||||||
# rich
|
|
||||||
# selenium
|
# selenium
|
||||||
# shillelagh
|
# shillelagh
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ apsw==3.50.1.0
|
|||||||
# shillelagh
|
# shillelagh
|
||||||
astroid==3.3.10
|
astroid==3.3.10
|
||||||
# via pylint
|
# via pylint
|
||||||
async-timeout==4.0.3
|
|
||||||
# via
|
|
||||||
# -c requirements/base.txt
|
|
||||||
# redis
|
|
||||||
attrs==25.3.0
|
attrs==25.3.0
|
||||||
# via
|
# via
|
||||||
# -c requirements/base.txt
|
# -c requirements/base.txt
|
||||||
@@ -180,13 +176,6 @@ et-xmlfile==2.0.0
|
|||||||
# via
|
# via
|
||||||
# -c requirements/base.txt
|
# -c requirements/base.txt
|
||||||
# openpyxl
|
# openpyxl
|
||||||
exceptiongroup==1.3.0
|
|
||||||
# via
|
|
||||||
# -c requirements/base.txt
|
|
||||||
# cattrs
|
|
||||||
# pytest
|
|
||||||
# trio
|
|
||||||
# trio-websocket
|
|
||||||
filelock==3.12.2
|
filelock==3.12.2
|
||||||
# via virtualenv
|
# via virtualenv
|
||||||
flask==2.3.3
|
flask==2.3.3
|
||||||
@@ -324,7 +313,6 @@ greenlet==3.1.1
|
|||||||
# apache-superset
|
# apache-superset
|
||||||
# gevent
|
# gevent
|
||||||
# shillelagh
|
# shillelagh
|
||||||
# sqlalchemy
|
|
||||||
grpcio==1.71.0
|
grpcio==1.71.0
|
||||||
# via
|
# via
|
||||||
# apache-superset
|
# apache-superset
|
||||||
@@ -682,7 +670,7 @@ python-dateutil==2.9.0.post0
|
|||||||
# pyhive
|
# pyhive
|
||||||
# shillelagh
|
# shillelagh
|
||||||
# trino
|
# trino
|
||||||
python-dotenv==1.1.1
|
python-dotenv==1.1.0
|
||||||
# via
|
# via
|
||||||
# -c requirements/base.txt
|
# -c requirements/base.txt
|
||||||
# apache-superset
|
# apache-superset
|
||||||
@@ -807,7 +795,7 @@ sqlalchemy==1.4.54
|
|||||||
# shillelagh
|
# shillelagh
|
||||||
# sqlalchemy-bigquery
|
# sqlalchemy-bigquery
|
||||||
# sqlalchemy-utils
|
# sqlalchemy-utils
|
||||||
sqlalchemy-bigquery==1.12.0
|
sqlalchemy-bigquery==1.15.0
|
||||||
# via apache-superset
|
# via apache-superset
|
||||||
sqlalchemy-utils==0.38.3
|
sqlalchemy-utils==0.38.3
|
||||||
# via
|
# via
|
||||||
@@ -830,11 +818,6 @@ tabulate==0.9.0
|
|||||||
# via
|
# via
|
||||||
# -c requirements/base.txt
|
# -c requirements/base.txt
|
||||||
# apache-superset
|
# apache-superset
|
||||||
tomli==2.2.1
|
|
||||||
# via
|
|
||||||
# coverage
|
|
||||||
# pylint
|
|
||||||
# pytest
|
|
||||||
tomlkit==0.13.3
|
tomlkit==0.13.3
|
||||||
# via pylint
|
# via pylint
|
||||||
tqdm==4.67.1
|
tqdm==4.67.1
|
||||||
@@ -857,13 +840,10 @@ typing-extensions==4.14.0
|
|||||||
# -c requirements/base.txt
|
# -c requirements/base.txt
|
||||||
# alembic
|
# alembic
|
||||||
# apache-superset
|
# apache-superset
|
||||||
# astroid
|
|
||||||
# cattrs
|
# cattrs
|
||||||
# exceptiongroup
|
|
||||||
# limits
|
# limits
|
||||||
# pyopenssl
|
# pyopenssl
|
||||||
# referencing
|
# referencing
|
||||||
# rich
|
|
||||||
# selenium
|
# selenium
|
||||||
# shillelagh
|
# shillelagh
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ const ExtraOptions = ({
|
|||||||
onExtraInputChange,
|
onExtraInputChange,
|
||||||
onExtraEditorChange,
|
onExtraEditorChange,
|
||||||
extraExtension,
|
extraExtension,
|
||||||
|
testedEngineInfo,
|
||||||
}: {
|
}: {
|
||||||
db: DatabaseObject | null;
|
db: DatabaseObject | null;
|
||||||
onInputChange: (
|
onInputChange: (
|
||||||
@@ -62,13 +63,14 @@ const ExtraOptions = ({
|
|||||||
) => void;
|
) => void;
|
||||||
onExtraEditorChange: Function;
|
onExtraEditorChange: Function;
|
||||||
extraExtension: DatabaseConnectionExtension | undefined;
|
extraExtension: DatabaseConnectionExtension | undefined;
|
||||||
|
testedEngineInfo?: any;
|
||||||
}) => {
|
}) => {
|
||||||
const expandableModalIsOpen = !!db?.expose_in_sqllab;
|
const expandableModalIsOpen = !!db?.expose_in_sqllab;
|
||||||
const createAsOpen = !!(db?.allow_ctas || db?.allow_cvas);
|
const createAsOpen = !!(db?.allow_ctas || db?.allow_cvas);
|
||||||
const isFileUploadSupportedByEngine =
|
// Use tested engine info if available, otherwise fall back to initial engine info
|
||||||
db?.engine_information?.supports_file_upload;
|
const engineInfo = testedEngineInfo || db?.engine_information;
|
||||||
const supportsDynamicCatalog =
|
const isFileUploadSupportedByEngine = engineInfo?.supports_file_upload;
|
||||||
db?.engine_information?.supports_dynamic_catalog;
|
const supportsDynamicCatalog = engineInfo?.supports_dynamic_catalog;
|
||||||
|
|
||||||
// JSON.parse will deep parse engine_params
|
// JSON.parse will deep parse engine_params
|
||||||
// if it's an object, and we want to keep it a string
|
// if it's an object, and we want to keep it a string
|
||||||
@@ -528,23 +530,29 @@ const ExtraOptions = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</StyledInputContainer>
|
</StyledInputContainer>
|
||||||
{isFileUploadSupportedByEngine && (
|
<StyledInputContainer
|
||||||
<StyledInputContainer
|
css={!db?.allow_file_upload ? no_margin_bottom : {}}
|
||||||
css={!db?.allow_file_upload ? no_margin_bottom : {}}
|
>
|
||||||
>
|
<div className="input-container">
|
||||||
<div className="input-container">
|
<Checkbox
|
||||||
<Checkbox
|
id="allow_file_upload"
|
||||||
id="allow_file_upload"
|
name="allow_file_upload"
|
||||||
name="allow_file_upload"
|
indeterminate={false}
|
||||||
indeterminate={false}
|
checked={!!db?.allow_file_upload}
|
||||||
checked={!!db?.allow_file_upload}
|
disabled={!isFileUploadSupportedByEngine}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
>
|
>
|
||||||
{t('Allow file uploads to database')}
|
{t('Allow file uploads to database')}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</div>
|
{!isFileUploadSupportedByEngine && (
|
||||||
</StyledInputContainer>
|
<InfoTooltip
|
||||||
)}
|
tooltip={t(
|
||||||
|
'File upload is not supported for this database engine'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</StyledInputContainer>
|
||||||
{isFileUploadSupportedByEngine && !!db?.allow_file_upload && (
|
{isFileUploadSupportedByEngine && !!db?.allow_file_upload && (
|
||||||
<StyledInputContainer css={no_margin_bottom}>
|
<StyledInputContainer css={no_margin_bottom}>
|
||||||
<div className="control-label">
|
<div className="control-label">
|
||||||
|
|||||||
@@ -607,6 +607,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||||||
const [editNewDb, setEditNewDb] = useState<boolean>(false);
|
const [editNewDb, setEditNewDb] = useState<boolean>(false);
|
||||||
const [isLoading, setLoading] = useState<boolean>(false);
|
const [isLoading, setLoading] = useState<boolean>(false);
|
||||||
const [testInProgress, setTestInProgress] = useState<boolean>(false);
|
const [testInProgress, setTestInProgress] = useState<boolean>(false);
|
||||||
|
const [testedEngineInfo, setTestedEngineInfo] = useState<any>(null);
|
||||||
const [passwords, setPasswords] = useState<Record<string, string>>({});
|
const [passwords, setPasswords] = useState<Record<string, string>>({});
|
||||||
const [sshTunnelPasswords, setSSHTunnelPasswords] = useState<
|
const [sshTunnelPasswords, setSSHTunnelPasswords] = useState<
|
||||||
Record<string, string>
|
Record<string, string>
|
||||||
@@ -735,6 +736,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||||||
addSuccessToast(errorMsg);
|
addSuccessToast(errorMsg);
|
||||||
setHasValidated(true);
|
setHasValidated(true);
|
||||||
},
|
},
|
||||||
|
(engineInfo: any) => {
|
||||||
|
setTestedEngineInfo(engineInfo);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -797,6 +801,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||||||
setSSHTunnelPrivateKeyPasswords({});
|
setSSHTunnelPrivateKeyPasswords({});
|
||||||
setConfirmedOverwrite(false);
|
setConfirmedOverwrite(false);
|
||||||
setUseSSHTunneling(undefined);
|
setUseSSHTunneling(undefined);
|
||||||
|
setTestedEngineInfo(null);
|
||||||
onHide();
|
onHide();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1771,6 +1776,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||||||
<ExtraOptions
|
<ExtraOptions
|
||||||
extraExtension={dbConfigExtraExtension}
|
extraExtension={dbConfigExtraExtension}
|
||||||
db={db as DatabaseObject}
|
db={db as DatabaseObject}
|
||||||
|
testedEngineInfo={testedEngineInfo}
|
||||||
onInputChange={(
|
onInputChange={(
|
||||||
e: CheckboxChangeEvent | React.ChangeEvent<HTMLInputElement>,
|
e: CheckboxChangeEvent | React.ChangeEvent<HTMLInputElement>,
|
||||||
) => {
|
) => {
|
||||||
@@ -2020,6 +2026,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
|||||||
<ExtraOptions
|
<ExtraOptions
|
||||||
extraExtension={dbConfigExtraExtension}
|
extraExtension={dbConfigExtraExtension}
|
||||||
db={db as DatabaseObject}
|
db={db as DatabaseObject}
|
||||||
|
testedEngineInfo={testedEngineInfo}
|
||||||
onInputChange={(e: CheckboxChangeEvent) => {
|
onInputChange={(e: CheckboxChangeEvent) => {
|
||||||
const { target } = e;
|
const { target } = e;
|
||||||
onChange(ActionType.InputChange, {
|
onChange(ActionType.InputChange, {
|
||||||
|
|||||||
@@ -712,14 +712,18 @@ export const testDatabaseConnection = (
|
|||||||
connection: Partial<DatabaseObject>,
|
connection: Partial<DatabaseObject>,
|
||||||
handleErrorMsg: (errorMsg: string) => void,
|
handleErrorMsg: (errorMsg: string) => void,
|
||||||
addSuccessToast: (arg0: string) => void,
|
addSuccessToast: (arg0: string) => void,
|
||||||
|
onEngineInfo?: (engineInfo: any) => void,
|
||||||
) => {
|
) => {
|
||||||
SupersetClient.post({
|
SupersetClient.post({
|
||||||
endpoint: 'api/v1/database/test_connection/',
|
endpoint: 'api/v1/database/test_connection/',
|
||||||
body: JSON.stringify(connection),
|
body: JSON.stringify(connection),
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
}).then(
|
}).then(
|
||||||
() => {
|
(response) => {
|
||||||
addSuccessToast(t('Connection looks good!'));
|
addSuccessToast(t('Connection looks good!'));
|
||||||
|
if (onEngineInfo && response?.json?.engine_information) {
|
||||||
|
onEngineInfo(response.json.engine_information);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
createErrorHandler((errMsg: Record<string, string[] | string> | string) => {
|
createErrorHandler((errMsg: Record<string, string[] | string> | string) => {
|
||||||
handleErrorMsg(t('ERROR: %s', parsedErrorMessage(errMsg)));
|
handleErrorMsg(t('ERROR: %s', parsedErrorMessage(errMsg)));
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class TestConnectionDatabaseCommand(BaseCommand):
|
|||||||
|
|
||||||
def run( # noqa: C901
|
def run( # noqa: C901
|
||||||
self,
|
self,
|
||||||
) -> None: # pylint: disable=too-many-statements,too-many-branches
|
) -> Database: # pylint: disable=too-many-statements,too-many-branches
|
||||||
self.validate()
|
self.validate()
|
||||||
ex_str = ""
|
ex_str = ""
|
||||||
ssh_tunnel = self._properties.get("ssh_tunnel")
|
ssh_tunnel = self._properties.get("ssh_tunnel")
|
||||||
@@ -168,6 +168,8 @@ class TestConnectionDatabaseCommand(BaseCommand):
|
|||||||
action=get_log_connection_action("test_connection_success", ssh_tunnel),
|
action=get_log_connection_action("test_connection_success", ssh_tunnel),
|
||||||
engine=database.db_engine_spec.__name__,
|
engine=database.db_engine_spec.__name__,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return database
|
||||||
|
|
||||||
except (NoSuchModuleError, ModuleNotFoundError) as ex:
|
except (NoSuchModuleError, ModuleNotFoundError) as ex:
|
||||||
event_logger.log_with_context(
|
event_logger.log_with_context(
|
||||||
|
|||||||
@@ -1258,6 +1258,17 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
|
|||||||
properties:
|
properties:
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
|
engine_information:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
supports_file_upload:
|
||||||
|
type: boolean
|
||||||
|
disable_ssh_tunneling:
|
||||||
|
type: boolean
|
||||||
|
supports_dynamic_catalog:
|
||||||
|
type: boolean
|
||||||
|
supports_oauth2:
|
||||||
|
type: boolean
|
||||||
400:
|
400:
|
||||||
$ref: '#/components/responses/400'
|
$ref: '#/components/responses/400'
|
||||||
422:
|
422:
|
||||||
@@ -1271,8 +1282,9 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
|
|||||||
except ValidationError as error:
|
except ValidationError as error:
|
||||||
return self.response_400(message=error.messages)
|
return self.response_400(message=error.messages)
|
||||||
try:
|
try:
|
||||||
TestConnectionDatabaseCommand(item).run()
|
database = TestConnectionDatabaseCommand(item).run()
|
||||||
return self.response(200, message="OK")
|
engine_information = database.db_engine_spec.get_public_information()
|
||||||
|
return self.response(200, message="OK", engine_information=engine_information)
|
||||||
except (SSHTunnelingNotEnabledError, SSHTunnelDatabasePortError) as ex:
|
except (SSHTunnelingNotEnabledError, SSHTunnelDatabasePortError) as ex:
|
||||||
return self.response_400(message=str(ex))
|
return self.response_400(message=str(ex))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user