Compare commits

...

1 Commits

Author SHA1 Message Date
Beto Dealmeida
5b51ae8bdc feat: improve file upload checkbox during DB creation 2025-07-28 18:37:23 -04:00
5 changed files with 58 additions and 25 deletions

View File

@@ -50,6 +50,7 @@ const ExtraOptions = ({
onExtraInputChange,
onExtraEditorChange,
extraExtension,
testedEngineInfo,
}: {
db: DatabaseObject | null;
onInputChange: (
@@ -62,13 +63,14 @@ const ExtraOptions = ({
) => void;
onExtraEditorChange: Function;
extraExtension: DatabaseConnectionExtension | undefined;
testedEngineInfo?: any;
}) => {
const expandableModalIsOpen = !!db?.expose_in_sqllab;
const createAsOpen = !!(db?.allow_ctas || db?.allow_cvas);
const isFileUploadSupportedByEngine =
db?.engine_information?.supports_file_upload;
const supportsDynamicCatalog =
db?.engine_information?.supports_dynamic_catalog;
// Use tested engine info if available, otherwise fall back to initial engine info
const engineInfo = testedEngineInfo || db?.engine_information;
const isFileUploadSupportedByEngine = engineInfo?.supports_file_upload;
const supportsDynamicCatalog = engineInfo?.supports_dynamic_catalog;
// JSON.parse will deep parse engine_params
// if it's an object, and we want to keep it a string
@@ -528,23 +530,29 @@ const ExtraOptions = ({
/>
</div>
</StyledInputContainer>
{isFileUploadSupportedByEngine && (
<StyledInputContainer
css={!db?.allow_file_upload ? no_margin_bottom : {}}
>
<div className="input-container">
<Checkbox
id="allow_file_upload"
name="allow_file_upload"
indeterminate={false}
checked={!!db?.allow_file_upload}
onChange={onInputChange}
>
{t('Allow file uploads to database')}
</Checkbox>
</div>
</StyledInputContainer>
)}
<StyledInputContainer
css={!db?.allow_file_upload ? no_margin_bottom : {}}
>
<div className="input-container">
<Checkbox
id="allow_file_upload"
name="allow_file_upload"
indeterminate={false}
checked={!!db?.allow_file_upload}
disabled={!isFileUploadSupportedByEngine}
onChange={onInputChange}
>
{t('Allow file uploads to database')}
</Checkbox>
{!isFileUploadSupportedByEngine && (
<InfoTooltip
tooltip={t(
'File upload is not supported for this database engine'
)}
/>
)}
</div>
</StyledInputContainer>
{isFileUploadSupportedByEngine && !!db?.allow_file_upload && (
<StyledInputContainer css={no_margin_bottom}>
<div className="control-label">

View File

@@ -607,6 +607,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
const [editNewDb, setEditNewDb] = useState<boolean>(false);
const [isLoading, setLoading] = useState<boolean>(false);
const [testInProgress, setTestInProgress] = useState<boolean>(false);
const [testedEngineInfo, setTestedEngineInfo] = useState<any>(null);
const [passwords, setPasswords] = useState<Record<string, string>>({});
const [sshTunnelPasswords, setSSHTunnelPasswords] = useState<
Record<string, string>
@@ -735,6 +736,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
addSuccessToast(errorMsg);
setHasValidated(true);
},
(engineInfo: any) => {
setTestedEngineInfo(engineInfo);
},
);
};
@@ -797,6 +801,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
setSSHTunnelPrivateKeyPasswords({});
setConfirmedOverwrite(false);
setUseSSHTunneling(undefined);
setTestedEngineInfo(null);
onHide();
};
@@ -1771,6 +1776,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
<ExtraOptions
extraExtension={dbConfigExtraExtension}
db={db as DatabaseObject}
testedEngineInfo={testedEngineInfo}
onInputChange={(
e: CheckboxChangeEvent | React.ChangeEvent<HTMLInputElement>,
) => {
@@ -2020,6 +2026,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
<ExtraOptions
extraExtension={dbConfigExtraExtension}
db={db as DatabaseObject}
testedEngineInfo={testedEngineInfo}
onInputChange={(e: CheckboxChangeEvent) => {
const { target } = e;
onChange(ActionType.InputChange, {

View File

@@ -712,14 +712,18 @@ export const testDatabaseConnection = (
connection: Partial<DatabaseObject>,
handleErrorMsg: (errorMsg: string) => void,
addSuccessToast: (arg0: string) => void,
onEngineInfo?: (engineInfo: any) => void,
) => {
SupersetClient.post({
endpoint: 'api/v1/database/test_connection/',
body: JSON.stringify(connection),
headers: { 'Content-Type': 'application/json' },
}).then(
() => {
(response) => {
addSuccessToast(t('Connection looks good!'));
if (onEngineInfo && response?.json?.engine_information) {
onEngineInfo(response.json.engine_information);
}
},
createErrorHandler((errMsg: Record<string, string[] | string> | string) => {
handleErrorMsg(t('ERROR: %s', parsedErrorMessage(errMsg)));

View File

@@ -91,7 +91,7 @@ class TestConnectionDatabaseCommand(BaseCommand):
def run( # noqa: C901
self,
) -> None: # pylint: disable=too-many-statements,too-many-branches
) -> Database: # pylint: disable=too-many-statements,too-many-branches
self.validate()
ex_str = ""
ssh_tunnel = self._properties.get("ssh_tunnel")
@@ -168,6 +168,8 @@ class TestConnectionDatabaseCommand(BaseCommand):
action=get_log_connection_action("test_connection_success", ssh_tunnel),
engine=database.db_engine_spec.__name__,
)
return database
except (NoSuchModuleError, ModuleNotFoundError) as ex:
event_logger.log_with_context(

View File

@@ -1258,6 +1258,17 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
properties:
message:
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:
$ref: '#/components/responses/400'
422:
@@ -1271,8 +1282,9 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
except ValidationError as error:
return self.response_400(message=error.messages)
try:
TestConnectionDatabaseCommand(item).run()
return self.response(200, message="OK")
database = TestConnectionDatabaseCommand(item).run()
engine_information = database.db_engine_spec.get_public_information()
return self.response(200, message="OK", engine_information=engine_information)
except (SSHTunnelingNotEnabledError, SSHTunnelDatabasePortError) as ex:
return self.response_400(message=str(ex))