fix(translate): add database_id to datasources response + auto-select on frontend

Root cause: fetch_available_datasources did not return database_id,
so frontend could not auto-set target_database_id when user selected
a datasource. User then had to manually pick a database on Target Config
tab — and could accidentally select the wrong one.

Backend changes:
- Add 'database_id' field to datasources response (from db_info.get('id'))
- Add _database_name tracking in SupersetSqlLabExecutor with getter
- Add database_name + database_backend to TargetSchemaValidationResponse
- Enrich resolve_database_id logging with database_name and all_keys

Frontend changes:
- In selectDatasource(), auto-set targetDatabaseId from ds.database_id
  when present, so the correct target DB is used for schema checks.
This commit is contained in:
2026-05-29 15:31:33 +03:00
parent 443751741b
commit 525e0a56af
6 changed files with 37 additions and 2 deletions

View File

@@ -279,6 +279,7 @@ class TestValidateTargetTableSchema:
instance = mock_executor.return_value
instance.resolve_database_id.return_value = None
instance.get_database_backend.return_value = "postgresql"
instance.get_database_name.return_value = "test_db"
instance.execute_and_poll.return_value = {
"status": "success",
"raw_response": {
@@ -299,6 +300,8 @@ class TestValidateTargetTableSchema:
assert len(response.missing_columns) == 0
assert len(response.expected_columns) == 4
assert len(response.actual_columns) == 4
assert response.database_name == "test_db"
assert response.database_backend == "postgresql"
@patch("src.plugins.translate.service_target_schema.SupersetSqlLabExecutor")
def test_missing_columns(self, mock_executor, build_request) -> None:
@@ -309,6 +312,7 @@ class TestValidateTargetTableSchema:
instance = mock_executor.return_value
instance.resolve_database_id.return_value = None
instance.get_database_backend.return_value = "postgresql"
instance.get_database_name.return_value = "test_db"
instance.execute_and_poll.return_value = {
"status": "success",
"raw_response": {
@@ -343,6 +347,7 @@ class TestValidateTargetTableSchema:
instance = mock_executor.return_value
instance.resolve_database_id.return_value = None
instance.get_database_backend.return_value = "clickhouse"
instance.get_database_name.return_value = "test_clickhouse"
instance.execute_and_poll.return_value = {
"status": "success",
"raw_response": {
@@ -366,6 +371,8 @@ class TestValidateTargetTableSchema:
assert response.all_present is True
assert len(response.missing_columns) == 0
assert len(response.actual_columns) == 8
assert response.database_name == "test_clickhouse"
assert response.database_backend == "clickhouse"
@patch("src.plugins.translate.service_target_schema.SupersetSqlLabExecutor")
def test_superset_failed_status(self, mock_executor, build_request) -> None:
@@ -376,6 +383,7 @@ class TestValidateTargetTableSchema:
instance = mock_executor.return_value
instance.resolve_database_id.return_value = None
instance.get_database_backend.return_value = "postgresql"
instance.get_database_name.return_value = "test_db"
instance.execute_and_poll.return_value = {
"status": "failed",
"error_message": "Query timeout",
@@ -396,6 +404,7 @@ class TestValidateTargetTableSchema:
instance = mock_executor.return_value
instance.resolve_database_id.return_value = None
instance.get_database_backend.return_value = "postgresql"
instance.get_database_name.return_value = "test_db"
instance.execute_and_poll.side_effect = ConnectionError("Superset API unavailable")
response = validate_target_table_schema(req, config_manager)
@@ -435,6 +444,7 @@ class TestValidateTargetTableSchema:
instance = mock_executor.return_value
instance.resolve_database_id.return_value = None
instance.get_database_backend.return_value = "postgresql"
instance.get_database_name.return_value = "test_db"
instance.execute_and_poll.return_value = {
"status": "success",
"raw_response": {

View File

@@ -254,6 +254,7 @@ class TranslateJobService:
"table_name": name,
"schema": ds.get("schema"),
"database_name": db_info.get("database_name", "Unknown"),
"database_id": db_info.get("id"), # <-- добавлено: ID БД для target_database_id
"database_dialect": dialect,
"description": ds.get("description", ""),
})

View File

@@ -237,6 +237,7 @@ def validate_target_table_schema(
executor = SupersetSqlLabExecutor(config_manager, req.environment_id)
executor.resolve_database_id(target_database_id=req.target_database_id)
backend = executor.get_database_backend() or ""
db_name = executor.get_database_name() or "unknown"
safe_schema = (req.target_schema or "public").replace("'", "''")
safe_table = req.target_table.replace("'", "''")
@@ -284,7 +285,7 @@ def validate_target_table_schema(
)
logger.reason("Executing SQL Lab query",
extra={"payload": {"sql": sql[:200], "backend": backend}})
extra={"payload": {"sql": sql[:200], "backend": backend, "database_name": db_name}})
result = executor.execute_and_poll(sql=sql, max_polls=15, poll_interval_seconds=1.0)
status = result.get("status", "")
@@ -297,6 +298,7 @@ def validate_target_table_schema(
table_exists=False, error=f"Superset SQL Lab error: {error_msg}",
expected_columns=expected, actual_columns=[],
missing_columns=expected, extra_columns=[], all_present=False,
database_name=db_name, database_backend=backend,
)
# Парсим data-строки результата (реальные колонки таблицы)
@@ -333,6 +335,7 @@ def validate_target_table_schema(
missing_columns=missing,
extra_columns=extra,
all_present=len(missing) == 0,
database_name=db_name, database_backend=backend,
)
except Exception as e:
@@ -343,6 +346,7 @@ def validate_target_table_schema(
table_exists=False, error=f"Failed to validate target schema: {e}",
expected_columns=expected, actual_columns=[],
missing_columns=expected, extra_columns=[], all_present=False,
database_name=db_name, database_backend=backend,
)
# #endregion validate_target_table_schema
# #endregion TargetSchemaValidation

View File

@@ -32,6 +32,7 @@ class SupersetSqlLabExecutor:
self._client: SupersetClient | None = None
self._database_id: int | None = None
self._database_backend: str | None = None
self._database_name: str | None = None
# region _get_client [TYPE Function]
# @PURPOSE Lazy-initialize SupersetClient for the configured environment.
@@ -74,10 +75,13 @@ class SupersetSqlLabExecutor:
result = db_info.get("result", db_info)
self._database_id = result.get("id", db_id)
self._database_backend = result.get("backend") or result.get("engine")
self._database_name = result.get("database_name", "")
logger.reason("Resolved database ID by target_database_id", {
"target_database_id": target_database_id,
"database_id": self._database_id,
"database_name": self._database_name,
"backend": self._database_backend,
"all_keys": list(result.keys()),
})
return self._database_id
except Exception as e:
@@ -99,9 +103,11 @@ class SupersetSqlLabExecutor:
if db.get("database_name", "").lower() == database_name.lower():
self._database_id = db["id"]
self._database_backend = db.get("backend")
self._database_name = db.get("database_name", "")
logger.reason("Resolved database ID by name", {
"database_name": database_name,
"database_id": self._database_id,
"actual_name": self._database_name,
"backend": self._database_backend,
})
return self._database_id
@@ -112,8 +118,9 @@ class SupersetSqlLabExecutor:
# Default: use first database
self._database_id = databases[0]["id"]
self._database_backend = databases[0].get("backend")
self._database_name = databases[0].get("database_name", "")
logger.reason("Using default database", {
"database_name": databases[0].get("database_name"),
"database_name": self._database_name,
"database_id": self._database_id,
"backend": self._database_backend,
})
@@ -127,6 +134,13 @@ class SupersetSqlLabExecutor:
return self._database_backend
# endregion get_database_backend
# region get_database_name [TYPE Function]
# @PURPOSE Return the cached database name.
# @POST Returns database name string or None if not yet resolved.
def get_database_name(self) -> str | None:
return self._database_name
# endregion get_database_name
# region execute_sql [TYPE Function]
# @PURPOSE Submit SQL to Superset SQL Lab and return execution tracking info.
# @PRE sql is valid SQL string. database_id is a valid Superset DB ID.

View File

@@ -705,6 +705,8 @@ class TargetSchemaValidationResponse(BaseModel):
missing_columns: list[TargetSchemaColumnInfo] = Field(default_factory=list, description="Expected columns NOT found in target table")
extra_columns: list[TargetSchemaColumnInfo] = Field(default_factory=list, description="Actual columns NOT in expected list")
all_present: bool = False
database_name: str | None = Field(default=None, description="Actual database name queried (from Superset)")
database_backend: str | None = Field(default=None, description="Actual database backend dialect (from Superset)")
# #endregion TargetSchemaValidationResponse