This is an automated email from the ASF dual-hosted git repository.

fisjac pushed a commit to branch fisjac/bigquery-import-bug
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/fisjac/bigquery-import-bug by 
this push:
     new 54f044b20d WIP investigating problem and possible solutions
54f044b20d is described below

commit 54f044b20dba29743af224adc0b1b9ee850b8068
Author: Jack Fisher <[email protected]>
AuthorDate: Fri Sep 27 14:31:25 2024 -0500

    WIP investigating problem and possible solutions
---
 .../src/features/databases/DatabaseModal/index.tsx     |  4 ++++
 superset-frontend/src/views/CRUD/hooks.ts              |  4 ++++
 superset-frontend/src/views/CRUD/utils.tsx             | 18 ++++++++++++++++++
 superset/commands/chart/importers/v1/__init__.py       |  9 +++++++++
 superset/commands/importers/v1/utils.py                |  4 ++++
 superset/databases/schemas.py                          | 16 ++++++++++++++++
 superset/models/core.py                                |  1 +
 7 files changed, 56 insertions(+)

diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx 
b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
index 53215570a6..d596ad61cb 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
@@ -592,6 +592,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> 
= ({
   const [importingModal, setImportingModal] = useState<boolean>(false);
   const [importingErrorMessage, setImportingErrorMessage] = useState<string>();
   const [passwordFields, setPasswordFields] = useState<string[]>([]);
+  const [encryptedExtraFields, setEncryptedExtraFields] = useState<string[]>(
+    [],
+  );
   const [sshTunnelPasswordFields, setSSHTunnelPasswordFields] = useState<
     string[]
   >([]);
@@ -1342,6 +1345,7 @@ const DatabaseModal: 
FunctionComponent<DatabaseModalProps> = ({
   const onDbImport = async (info: UploadChangeParam) => {
     setImportingErrorMessage('');
     setPasswordFields([]);
+    setEncryptedExtraFields([]);
     setSSHTunnelPasswordFields([]);
     setSSHTunnelPrivateKeyFields([]);
     setSSHTunnelPrivateKeyPasswordFields([]);
diff --git a/superset-frontend/src/views/CRUD/hooks.ts 
b/superset-frontend/src/views/CRUD/hooks.ts
index 6fa167d934..f151e6ae16 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -30,6 +30,7 @@ import {
   createErrorHandler,
   getAlreadyExists,
   getPasswordsNeeded,
+  getEncryptedExtraNeeded,
   hasTerminalValidation,
   getSSHPasswordsNeeded,
   getSSHPrivateKeysNeeded,
@@ -400,6 +401,7 @@ export function useSingleViewResource<D extends object = 
any>(
 interface ImportResourceState {
   loading: boolean;
   passwordsNeeded: string[];
+  encryptedExtraNeeded: string[];
   alreadyExists: string[];
   sshPasswordNeeded: string[];
   sshPrivateKeyNeeded: string[];
@@ -415,6 +417,7 @@ export function useImportResource(
   const [state, setState] = useState<ImportResourceState>({
     loading: false,
     passwordsNeeded: [],
+    encryptedExtraNeeded: [],
     alreadyExists: [],
     sshPasswordNeeded: [],
     sshPrivateKeyNeeded: [],
@@ -533,6 +536,7 @@ export function useImportResource(
             } else {
               updateState({
                 passwordsNeeded: getPasswordsNeeded(error.errors),
+                encryptedExtraNeeded: getEncryptedExtraNeeded(error.errors),
                 sshPasswordNeeded: getSSHPasswordsNeeded(error.errors),
                 sshPrivateKeyNeeded: getSSHPrivateKeysNeeded(error.errors),
                 sshPrivateKeyPasswordNeeded: getSSHPrivateKeyPasswordsNeeded(
diff --git a/superset-frontend/src/views/CRUD/utils.tsx 
b/superset-frontend/src/views/CRUD/utils.tsx
index 7b9274a904..50b797edfb 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -395,6 +395,14 @@ const isNeedsPassword = (payload: any) =>
     (e: string) => e === 'Must provide a password for the database',
   );
 
+export /* eslint-disable no-underscore-dangle */
+const isNeedsEncryptedExtra = (payload: any) =>
+  typeof payload === 'object' &&
+  Array.isArray(payload._schema) &&
+  !!payload._schema?.find(
+    (e: string) => e === 'Must provide encrypted credentials for the database',
+  );
+
 export /* eslint-disable no-underscore-dangle */
 const isNeedsSSHPassword = (payload: any) =>
   typeof payload === 'object' &&
@@ -433,6 +441,15 @@ export const getPasswordsNeeded = (errors: Record<string, 
any>[]) =>
     )
     .flat();
 
+export const getEncryptedExtraNeeded = (errors: Record<string, any>[]) =>
+  errors
+    .map(error =>
+      Object.entries(error.extra)
+        .filter(([, payload]) => isNeedsEncryptedExtra(payload))
+        .map(([fileName]) => fileName),
+    )
+    .flat();
+
 export const getSSHPasswordsNeeded = (errors: Record<string, any>[]) =>
   errors
     .map(error =>
@@ -482,6 +499,7 @@ export const hasTerminalValidation = (errors: 
Record<string, any>[]) =>
     return !noIssuesCodes.every(
       ([, payload]) =>
         isNeedsPassword(payload) ||
+        isNeedsEncryptedExtra(payload) ||
         isAlreadyExists(payload) ||
         isNeedsSSHPassword(payload) ||
         isNeedsSSHPrivateKey(payload) ||
diff --git a/superset/commands/chart/importers/v1/__init__.py 
b/superset/commands/chart/importers/v1/__init__.py
index 89fe5e7a70..02a1b9f676 100644
--- a/superset/commands/chart/importers/v1/__init__.py
+++ b/superset/commands/chart/importers/v1/__init__.py
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import logging
 from typing import Any
 
 from marshmallow import Schema
@@ -31,6 +32,8 @@ from superset.daos.chart import ChartDAO
 from superset.databases.schemas import ImportV1DatabaseSchema
 from superset.datasets.schemas import ImportV1DatasetSchema
 
+logger = logging.getLogger(__name__)
+
 
 class ImportChartsCommand(ImportModelsCommand):
     """Import charts"""
@@ -49,22 +52,28 @@ class ImportChartsCommand(ImportModelsCommand):
     def _import(configs: dict[str, Any], overwrite: bool = False) -> None:
         # discover datasets associated with charts
         dataset_uuids: set[str] = set()
+        logger.info("parsing datasets")
         for file_name, config in configs.items():
             if file_name.startswith("charts/"):
                 dataset_uuids.add(config["dataset_uuid"])
+        logger.info("datasets parsed: %s", dataset_uuids)
 
         # discover databases associated with datasets
+        logger.info("discovering databases associated with datasets")
+
         database_uuids: set[str] = set()
         for file_name, config in configs.items():
             if file_name.startswith("datasets/") and config["uuid"] in 
dataset_uuids:
                 database_uuids.add(config["database_uuid"])
 
         # import related databases
+        logger.info("importing related dbs")
         database_ids: dict[str, int] = {}
         for file_name, config in configs.items():
             if file_name.startswith("databases/") and config["uuid"] in 
database_uuids:
                 database = import_database(config, overwrite=False)
                 database_ids[str(database.uuid)] = database.id
+        logger.info("related dbs: %s", database_ids)
 
         # import datasets with the correct parent ref
         datasets: dict[str, SqlaTable] = {}
diff --git a/superset/commands/importers/v1/utils.py 
b/superset/commands/importers/v1/utils.py
index 51ab99271c..2497828f9b 100644
--- a/superset/commands/importers/v1/utils.py
+++ b/superset/commands/importers/v1/utils.py
@@ -184,6 +184,10 @@ def load_configs(
                         db_ssh_tunnel_priv_key_passws[config["uuid"]]
                     )
 
+                logger.info(
+                    "attempting to load content into schema %s %s", schema, 
content
+                )
+
                 schema.load(config)
                 configs[file_name] = config
             except ValidationError as exc:
diff --git a/superset/databases/schemas.py b/superset/databases/schemas.py
index 27eb043eb1..6d8dc4c69b 100644
--- a/superset/databases/schemas.py
+++ b/superset/databases/schemas.py
@@ -20,6 +20,7 @@
 from __future__ import annotations
 
 import inspect
+import logging
 import os
 from pathlib import Path
 from typing import Any, TypedDict
@@ -55,6 +56,8 @@ from superset.security.analytics_db_safety import 
check_sqlalchemy_uri
 from superset.utils import json
 from superset.utils.core import markdown, parse_ssl_cert
 
+logger = logging.getLogger(__name__)
+
 database_schemas_query_schema = {
     "type": "object",
     "properties": {
@@ -859,6 +862,7 @@ class ImportV1DatabaseSchema(Schema):
     allow_csv_upload = fields.Boolean()
     impersonate_user = fields.Boolean()
     extra = fields.Nested(ImportV1DatabaseExtraSchema)
+    encrypted_extra = fields.String(allow_none=True)
     uuid = fields.UUID(required=True)
     version = fields.String(required=True)
     is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
@@ -878,6 +882,18 @@ class ImportV1DatabaseSchema(Schema):
         if password == PASSWORD_MASK and data.get("password") is None:
             raise ValidationError("Must provide a password for the database")
 
+    @validates_schema
+    def validate_encrypted_extra(self, data: dict[str, Any], **kwargs: Any) -> 
None:
+        """If encrypted extra contains masked data, encrypted extra is 
required"""
+        uuid = data["uuid"]
+        existing = db.session.query(Database).filter_by(uuid=uuid).first()
+        if existing:
+            return
+        encrypted_extra = data.get("encrypted_extra", None)
+        logger.info("encrypted_extra loaded as %s", encrypted_extra)
+        if not encrypted_extra:
+            raise ValidationError("Must provide encrypted credentials for the 
database")
+
     @validates_schema
     def validate_ssh_tunnel_credentials(
         self, data: dict[str, Any], **kwargs: Any
diff --git a/superset/models/core.py b/superset/models/core.py
index 5d3a6ea74d..5907008e0a 100755
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -172,6 +172,7 @@ class Database(Model, AuditMixinNullable, 
ImportExportMixin):  # pylint: disable
         "allow_file_upload",
         "extra",
         "impersonate_user",
+        "encrypted_extra",
     ]
     extra_import_fields = [
         "password",

Reply via email to