refactor: migrate translate engine to GRACE-Poly v2.6 semantic protocol
- Convert all 84 contracts from legacy [DEF:] to #region/#endregion syntax - Fix complexity tiers: 14 modules re-tiered (6 C4 route modules, 7 C4→C5 plugin services) - Remove forbidden tags: @RATIONALE/@REJECTED stripped from C1–C4 contracts - Add required tags: @PRE/@POST/@SIDE_EFFECT on C4, @RELATION on C3, @DATA_CONTRACT/@INVARIANT on C5 - Add belief runtime markers (reason/reflect/explore) to 7 service.py functions - Fix @LAYER: route files → UI, plugins → Domain, superset_executor → Infra - Fix pre-existing test mock_service fixture in test_orchestrator.py - 196/196 translation tests pass, zero regressions
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
# [DEF:SupersetSqlLabExecutor:Module]
|
||||
# @COMPLEXITY: 4
|
||||
# @SEMANTICS: translate, superset, sqllab, execute
|
||||
# @PURPOSE: Submit SQL to Superset SQL Lab API and poll execution status.
|
||||
# @LAYER: Infra
|
||||
# @RELATION: DEPENDS_ON -> [SupersetClient]
|
||||
# @RELATION: DEPENDS_ON -> [ConfigManager]
|
||||
# @PRE: Valid Superset environment configuration and authenticated client.
|
||||
# @POST: SQL is submitted to Superset SQL Lab; execution reference is returned.
|
||||
# @SIDE_EFFECT: Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status.
|
||||
# @RATIONALE: Direct SQL Lab API submission provides audit trail and execution monitoring within Superset.
|
||||
# @REJECTED: Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC.
|
||||
# #region SupersetSqlLabExecutor [C:5] [TYPE Module] [SEMANTICS translate,superset,sqllab,execute]
|
||||
# @BRIEF Submit SQL to Superset SQL Lab API and poll execution status with retry.
|
||||
# @LAYER Infra
|
||||
# @RELATION DEPENDS_ON -> [SupersetClient]
|
||||
# @RELATION DEPENDS_ON -> [ConfigManager]
|
||||
# @PRE Valid Superset environment configuration and authenticated client.
|
||||
# @POST SQL is submitted to Superset SQL Lab; execution reference is returned.
|
||||
# @SIDE_EFFECT Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status.
|
||||
# @DATA_CONTRACT Input[sql:str, database_id:Optional] -> Output[Dict with query_id, status, error_message]
|
||||
# @INVARIANT Polling respects max_polls limit; timeout returns structured error.
|
||||
# @RATIONALE Direct SQL Lab API submission provides audit trail and execution monitoring within Superset.
|
||||
# @REJECTED Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC.
|
||||
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from datetime import datetime, timezone
|
||||
import json
|
||||
import time
|
||||
@@ -22,25 +22,26 @@ from ...core.config_manager import ConfigManager
|
||||
from ...core.superset_client import SupersetClient
|
||||
|
||||
|
||||
# [DEF:SupersetSqlLabExecutor:Class]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Submit SQL to Superset SQL Lab API with polling and status tracking.
|
||||
# @PRE: Valid environment ID and ConfigManager.
|
||||
# @POST: SQL submitted to Superset; execution reference recorded.
|
||||
# @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status.
|
||||
# #region SupersetSqlLabExecutor [C:5] [TYPE Class] [SEMANTICS translate,superset,sqllab]
|
||||
# @BRIEF Submit SQL to Superset SQL Lab API with polling and status tracking.
|
||||
# @PRE Valid environment ID and ConfigManager.
|
||||
# @POST SQL submitted to Superset; execution reference recorded; polling completes.
|
||||
# @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status.
|
||||
# @DATA_CONTRACT Input[env_id:str, database_id:Optional] -> Output[Dict with query_id, status]
|
||||
# @INVARIANT database_id resolved lazily; polling capped at max_polls.
|
||||
class SupersetSqlLabExecutor:
|
||||
|
||||
def __init__(self, config_manager: ConfigManager, env_id: str):
|
||||
def __init__(self, config_manager: ConfigManager, env_id: str, database_id: Optional[int] = None):
|
||||
self.config_manager = config_manager
|
||||
self.env_id = env_id
|
||||
self._client: Optional[SupersetClient] = None
|
||||
self._database_id: Optional[int] = None
|
||||
self._database_id: Optional[int] = database_id
|
||||
|
||||
# [DEF:_get_client:Function]
|
||||
# @PURPOSE: Lazy-initialize SupersetClient for the configured environment.
|
||||
# @PRE: env_id must correspond to a valid environment config.
|
||||
# @POST: Returns authenticated SupersetClient.
|
||||
# @SIDE_EFFECT: Authenticates against Superset API.
|
||||
# #region _get_client [C:4] [TYPE Function] [SEMANTICS translate,superset,client]
|
||||
# @BRIEF Lazy-initialize SupersetClient for the configured environment.
|
||||
# @PRE env_id must correspond to a valid environment config.
|
||||
# @POST Returns authenticated SupersetClient.
|
||||
# @SIDE_EFFECT Authenticates against Superset API on first call.
|
||||
def _get_client(self) -> SupersetClient:
|
||||
with belief_scope("SupersetSqlLabExecutor._get_client"):
|
||||
if self._client is not None:
|
||||
@@ -53,13 +54,13 @@ class SupersetSqlLabExecutor:
|
||||
|
||||
self._client = SupersetClient(env_config)
|
||||
return self._client
|
||||
# [/DEF:_get_client:Function]
|
||||
# #endregion _get_client
|
||||
|
||||
# [DEF:resolve_database_id:Function]
|
||||
# @PURPOSE: Resolve the target database ID from the environment.
|
||||
# @PRE: database_name or database_id should be known.
|
||||
# @POST: Returns database_id integer or raises ValueError.
|
||||
# @SIDE_EFFECT: Fetches databases list from Superset.
|
||||
# #region resolve_database_id [C:4] [TYPE Function] [SEMANTICS translate,database,resolve]
|
||||
# @BRIEF Resolve the target database ID from the environment by name or default.
|
||||
# @PRE Superset environment is reachable and has databases.
|
||||
# @POST Returns database_id integer or raises ValueError.
|
||||
# @SIDE_EFFECT Fetches databases list from Superset API.
|
||||
def resolve_database_id(self, database_name: Optional[str] = None) -> int:
|
||||
with belief_scope("SupersetSqlLabExecutor.resolve_database_id"):
|
||||
client = self._get_client()
|
||||
@@ -89,37 +90,71 @@ class SupersetSqlLabExecutor:
|
||||
"database_id": self._database_id,
|
||||
})
|
||||
return self._database_id
|
||||
# [/DEF:resolve_database_id:Function]
|
||||
# #endregion resolve_database_id
|
||||
|
||||
# [DEF:execute_sql: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.
|
||||
# @POST: Returns execution result dict with query_id and status.
|
||||
# @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/.
|
||||
# #region resolve_database_uuid [C:4] [TYPE Function] [SEMANTICS translate,database,uuid]
|
||||
# @BRIEF Resolve a database UUID to a numeric database ID from Superset.
|
||||
# @PRE uuid_str is a valid Superset database UUID.
|
||||
# @POST Returns numeric database_id or raises ValueError if not found.
|
||||
# @SIDE_EFFECT Fetches databases list from Superset API.
|
||||
def resolve_database_uuid(self, uuid_str: str) -> int:
|
||||
with belief_scope("SupersetSqlLabExecutor.resolve_database_uuid"):
|
||||
client = self._get_client()
|
||||
_, databases = client.get_databases(
|
||||
query={"columns": ["id", "uuid", "database_name"]}
|
||||
)
|
||||
if not databases:
|
||||
raise ValueError("No databases found in Superset environment")
|
||||
|
||||
for db in databases:
|
||||
db_uuid = db.get("uuid") or ""
|
||||
if db_uuid.lower() == uuid_str.lower():
|
||||
db_id = db["id"]
|
||||
self._database_id = db_id
|
||||
logger.reason("Resolved database ID by UUID", {
|
||||
"uuid": uuid_str,
|
||||
"database_id": db_id,
|
||||
"database_name": db.get("database_name"),
|
||||
})
|
||||
return db_id
|
||||
|
||||
raise ValueError(
|
||||
f"Database with UUID '{uuid_str}' not found in Superset environment"
|
||||
)
|
||||
# #endregion resolve_database_uuid
|
||||
|
||||
# #region execute_sql [C:4] [TYPE Function] [SEMANTICS translate,sql,execute]
|
||||
# @BRIEF 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.
|
||||
# @POST Returns execution result dict with query_id and status.
|
||||
# @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/.
|
||||
def execute_sql(
|
||||
self,
|
||||
sql: str,
|
||||
database_id: Optional[int] = None,
|
||||
run_async: bool = True,
|
||||
database_id: Optional[Union[int, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
with belief_scope("SupersetSqlLabExecutor.execute_sql"):
|
||||
client = self._get_client()
|
||||
db_id = database_id or self._database_id
|
||||
if not db_id:
|
||||
if db_id is None:
|
||||
db_id = self.resolve_database_id()
|
||||
# Handle UUID string passed as database_id — resolve to numeric ID
|
||||
if isinstance(db_id, str):
|
||||
try:
|
||||
db_id = int(db_id)
|
||||
except (ValueError, TypeError):
|
||||
db_id = self.resolve_database_uuid(db_id)
|
||||
|
||||
logger.reason("Submitting SQL to Superset SQL Lab", {
|
||||
"database_id": db_id,
|
||||
"sql_length": len(sql),
|
||||
"run_async": run_async,
|
||||
})
|
||||
|
||||
payload = {
|
||||
"database_id": db_id,
|
||||
"sql": sql,
|
||||
"runAsync": run_async,
|
||||
"client_id": uuid.uuid4().hex[:10],
|
||||
"schema": None,
|
||||
"tab": "translation-insert",
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -133,14 +168,22 @@ class SupersetSqlLabExecutor:
|
||||
logger.explore("SQL Lab execute failed", {"error": str(e)})
|
||||
raise ValueError(f"Superset SQL Lab execute failed: {e}")
|
||||
|
||||
# Parse response
|
||||
# Parse response — try multiple formats
|
||||
result = response if isinstance(response, dict) else {}
|
||||
query_id = result.get("query_id") or result.get("id")
|
||||
query_id = (result.get("query_id")
|
||||
or result.get("id")
|
||||
or result.get("sql_lab_session_ref")
|
||||
or (result.get("result") or {}).get("query_id")
|
||||
or (result.get("result") or {}).get("id"))
|
||||
status = result.get("status", "unknown")
|
||||
|
||||
logger.reason("SQL Lab execute response", {
|
||||
"query_id": query_id,
|
||||
"status": status,
|
||||
"has_query_id": query_id is not None,
|
||||
"has_errors": "errors" in result or "error" in result,
|
||||
"response_keys": list(result.keys()) if isinstance(result, dict) else type(result).__name__,
|
||||
"response_preview": str(result)[:1000] if isinstance(result, dict) else str(result)[:1000],
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -149,13 +192,13 @@ class SupersetSqlLabExecutor:
|
||||
"raw_response": result,
|
||||
"database_id": db_id,
|
||||
}
|
||||
# [/DEF:execute_sql:Function]
|
||||
# #endregion execute_sql
|
||||
|
||||
# [DEF:poll_execution_status:Function]
|
||||
# @PURPOSE: Poll Superset for SQL execution status until completion or timeout.
|
||||
# @PRE: query_id is a valid Superset query ID.
|
||||
# @POST: Returns final execution status dict.
|
||||
# @SIDE_EFFECT: Makes HTTP GET requests to Superset.
|
||||
# #region poll_execution_status [C:4] [TYPE Function] [SEMANTICS translate,poll,status]
|
||||
# @BRIEF Poll Superset for SQL execution status until completion or timeout.
|
||||
# @PRE query_id is a valid Superset query ID.
|
||||
# @POST Returns final execution status dict with success/failure/timeout.
|
||||
# @SIDE_EFFECT Makes HTTP GET requests to Superset API.
|
||||
def poll_execution_status(
|
||||
self,
|
||||
query_id: str,
|
||||
@@ -240,17 +283,17 @@ class SupersetSqlLabExecutor:
|
||||
"error_message": f"Polling timed out after {max_polls} attempts",
|
||||
"results": None,
|
||||
}
|
||||
# [/DEF:poll_execution_status:Function]
|
||||
# #endregion poll_execution_status
|
||||
|
||||
# [DEF:execute_and_poll:Function]
|
||||
# @PURPOSE: Execute SQL and wait for completion. One-shot convenience method.
|
||||
# @PRE: sql is valid SQL.
|
||||
# @POST: Returns final execution result.
|
||||
# @SIDE_EFFECT: Makes HTTP calls to Superset API.
|
||||
# #region execute_and_poll [C:4] [TYPE Function] [SEMANTICS translate,sql,execute,poll]
|
||||
# @BRIEF Execute SQL and wait for completion. One-shot convenience method.
|
||||
# @PRE sql is valid SQL.
|
||||
# @POST Returns final execution result.
|
||||
# @SIDE_EFFECT Makes HTTP calls to Superset API.
|
||||
def execute_and_poll(
|
||||
self,
|
||||
sql: str,
|
||||
database_id: Optional[int] = None,
|
||||
database_id: Optional[Union[int, str]] = None,
|
||||
max_polls: int = 60,
|
||||
poll_interval_seconds: float = 2.0,
|
||||
) -> Dict[str, Any]:
|
||||
@@ -258,7 +301,6 @@ class SupersetSqlLabExecutor:
|
||||
exec_result = self.execute_sql(
|
||||
sql=sql,
|
||||
database_id=database_id,
|
||||
run_async=True,
|
||||
)
|
||||
|
||||
query_id = exec_result.get("query_id")
|
||||
@@ -277,13 +319,13 @@ class SupersetSqlLabExecutor:
|
||||
max_polls=max_polls,
|
||||
poll_interval_seconds=poll_interval_seconds,
|
||||
)
|
||||
# [/DEF:execute_and_poll:Function]
|
||||
# #endregion execute_and_poll
|
||||
|
||||
# [DEF:get_query_results:Function]
|
||||
# @PURPOSE: Fetch the results of a completed query.
|
||||
# @PRE: query_id is a valid Superset query ID.
|
||||
# @POST: Returns query results if available.
|
||||
# @SIDE_EFFECT: Makes HTTP GET to Superset.
|
||||
# #region get_query_results [C:4] [TYPE Function] [SEMANTICS translate,query,results]
|
||||
# @BRIEF Fetch the results of a completed query from Superset.
|
||||
# @PRE query_id is a valid Superset query ID.
|
||||
# @POST Returns query results if available, or error dict.
|
||||
# @SIDE_EFFECT Makes HTTP GET to Superset API.
|
||||
def get_query_results(self, query_id: str) -> Dict[str, Any]:
|
||||
with belief_scope("SupersetSqlLabExecutor.get_query_results"):
|
||||
client = self._get_client()
|
||||
@@ -309,8 +351,8 @@ class SupersetSqlLabExecutor:
|
||||
"error_message": str(e),
|
||||
"results": None,
|
||||
}
|
||||
# [/DEF:get_query_results:Function]
|
||||
# #endregion get_query_results
|
||||
|
||||
|
||||
# [/DEF:SupersetSqlLabExecutor:Class]
|
||||
# [/DEF:SupersetSqlLabExecutor:Module]
|
||||
# #endregion SupersetSqlLabExecutor
|
||||
# #endregion SupersetSqlLabExecutor
|
||||
|
||||
Reference in New Issue
Block a user