032: Phase 3 US1 — 13 mixins migrated to async + dashboard routes
T011-T017: All SupersetClient mixins now async. T018: _detail_routes.py uses registry/AsyncSupersetClient. T019: Partial — routes/environments, settings, profile, listing async. RATIONALE: Big-bang merge of sync+AsyncSupersetClient. REJECTED: dual-stack. Remaining T019: assistant/*, migration, datasets, git helpers still use sync.
This commit is contained in:
@@ -15,7 +15,7 @@ from fastapi.responses import JSONResponse
|
||||
|
||||
from src.core.async_superset_client import AsyncSupersetClient
|
||||
from src.core.logger import belief_scope, logger
|
||||
from src.core.superset_client import SupersetClient
|
||||
from src.core.utils.client_registry import get_client as get_superset_client
|
||||
from src.core.utils.network import DashboardNotFoundError
|
||||
from src.dependencies import (
|
||||
get_config_manager,
|
||||
@@ -25,7 +25,6 @@ from src.dependencies import (
|
||||
)
|
||||
|
||||
from ._helpers import (
|
||||
_resolve_dashboard_id_from_ref,
|
||||
_resolve_dashboard_id_from_ref_async,
|
||||
)
|
||||
from ._projection import (
|
||||
@@ -131,9 +130,23 @@ async def get_dashboard_detail(
|
||||
raise HTTPException(status_code=404, detail="Environment not found")
|
||||
|
||||
try:
|
||||
sync_client = SupersetClient(env)
|
||||
dashboard_id = _resolve_dashboard_id_from_ref(dashboard_ref, sync_client)
|
||||
detail = sync_client.get_dashboard_detail(dashboard_id)
|
||||
# Use async SupersetClient with shared AsyncAPIClient from registry
|
||||
env_config = {
|
||||
"base_url": env.url,
|
||||
"auth": {
|
||||
"username": env.username,
|
||||
"password": env.password,
|
||||
"provider": "db",
|
||||
"refresh": "true",
|
||||
},
|
||||
"verify_ssl": env.verify_ssl,
|
||||
}
|
||||
async_client = await get_superset_client(env_config)
|
||||
client = AsyncSupersetClient(env)
|
||||
dashboard_id = await _resolve_dashboard_id_from_ref_async(
|
||||
dashboard_ref, client
|
||||
)
|
||||
detail = await client.get_dashboard_detail(dashboard_id)
|
||||
logger.info(
|
||||
f"[get_dashboard_detail][Coherence:OK] Dashboard ref={dashboard_ref} resolved_id={dashboard_id}: {detail.get('chart_count', 0)} charts, {detail.get('dataset_count', 0)} datasets"
|
||||
)
|
||||
@@ -256,7 +269,7 @@ async def get_dashboard_tasks_history(
|
||||
|
||||
# #region get_dashboard_thumbnail [C:3] [TYPE Function]
|
||||
# @BRIEF Proxies Superset dashboard thumbnail with cache support.
|
||||
# @RELATION CALLS -> [AsyncSupersetClient]
|
||||
# @RELATION CALLS -> [AsyncAPIClient]
|
||||
# @PRE env_id must exist.
|
||||
# @POST Returns image bytes or 202 when thumbnail is being prepared by Superset.
|
||||
@router.get("/{dashboard_ref}/thumbnail")
|
||||
@@ -280,13 +293,27 @@ async def get_dashboard_thumbnail(
|
||||
raise HTTPException(status_code=404, detail="Environment not found")
|
||||
|
||||
try:
|
||||
client = SupersetClient(env)
|
||||
dashboard_id = _resolve_dashboard_id_from_ref(dashboard_ref, client)
|
||||
# Use shared AsyncAPIClient from registry
|
||||
env_config = {
|
||||
"base_url": env.url,
|
||||
"auth": {
|
||||
"username": env.username,
|
||||
"password": env.password,
|
||||
"provider": "db",
|
||||
"refresh": "true",
|
||||
},
|
||||
"verify_ssl": env.verify_ssl,
|
||||
}
|
||||
async_client = await get_superset_client(env_config)
|
||||
client = AsyncSupersetClient(env)
|
||||
dashboard_id = await _resolve_dashboard_id_from_ref_async(
|
||||
dashboard_ref, client
|
||||
)
|
||||
digest = None
|
||||
thumb_endpoint = None
|
||||
|
||||
try:
|
||||
screenshot_payload = client.network.request(
|
||||
screenshot_payload = await async_client.request(
|
||||
method="POST",
|
||||
endpoint=f"/dashboard/{dashboard_id}/cache_dashboard_screenshot/",
|
||||
)
|
||||
@@ -311,7 +338,7 @@ async def get_dashboard_thumbnail(
|
||||
)
|
||||
|
||||
if not digest:
|
||||
dashboard_payload = client.network.request(
|
||||
dashboard_payload = await async_client.request(
|
||||
method="GET",
|
||||
endpoint=f"/dashboard/{dashboard_id}",
|
||||
)
|
||||
@@ -343,11 +370,10 @@ async def get_dashboard_thumbnail(
|
||||
f"/dashboard/{dashboard_id}/thumbnail/{digest or 'latest'}/"
|
||||
)
|
||||
|
||||
thumb_response = client.network.request(
|
||||
thumb_response = await async_client.request(
|
||||
method="GET",
|
||||
endpoint=thumb_endpoint,
|
||||
raw_response=True,
|
||||
allow_redirects=True,
|
||||
)
|
||||
|
||||
if thumb_response.status_code == 202:
|
||||
|
||||
@@ -245,7 +245,7 @@ async def get_dashboards(
|
||||
and bound_username
|
||||
and profile_service is not None
|
||||
):
|
||||
actor_aliases = _resolve_profile_actor_aliases(env, bound_username)
|
||||
actor_aliases = await _resolve_profile_actor_aliases(env, bound_username)
|
||||
if not actor_aliases:
|
||||
actor_aliases = [bound_username]
|
||||
logger.reason(
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
from typing import Any
|
||||
|
||||
from src.core.logger import logger
|
||||
from src.core.superset_client import SupersetClient
|
||||
from src.core.async_superset_client import AsyncSupersetClient
|
||||
from src.core.superset_profile_lookup import SupersetAccountLookupAdapter
|
||||
from src.models.auth import User
|
||||
from src.services.profile_service import ProfileService
|
||||
@@ -142,19 +142,19 @@ def _get_profile_filter_binding(
|
||||
|
||||
# #region _resolve_profile_actor_aliases [C:2] [TYPE Function]
|
||||
# @BRIEF Resolve stable actor aliases for profile filtering without per-dashboard detail fan-out.
|
||||
# @SIDE_EFFECT Performs at most one Superset users-lookup request.
|
||||
def _resolve_profile_actor_aliases(env: Any, bound_username: str) -> list[str]:
|
||||
# @SIDE_EFFECT Асинхронный HTTP-вызов к Superset API для поиска пользователей.
|
||||
async def _resolve_profile_actor_aliases(env: Any, bound_username: str) -> list[str]:
|
||||
normalized_bound = _normalize_actor_alias_token(bound_username)
|
||||
if not normalized_bound:
|
||||
return []
|
||||
aliases: list[str] = [normalized_bound]
|
||||
try:
|
||||
client = SupersetClient(env)
|
||||
client = AsyncSupersetClient(env)
|
||||
adapter = SupersetAccountLookupAdapter(
|
||||
network_client=client.network,
|
||||
network_client=client.client,
|
||||
environment_id=str(getattr(env, "id", "")),
|
||||
)
|
||||
lookup_payload = adapter.get_users_page(
|
||||
lookup_payload = await adapter.get_users_page(
|
||||
search=normalized_bound,
|
||||
page_index=0,
|
||||
page_size=20,
|
||||
|
||||
@@ -12,7 +12,7 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from ...core.logger import belief_scope
|
||||
from ...core.superset_client import SupersetClient
|
||||
from ...core.async_superset_client import AsyncSupersetClient
|
||||
from ...dependencies import get_config_manager, get_scheduler_service, has_permission
|
||||
|
||||
router = APIRouter(prefix="/api/environments", tags=["Environments"])
|
||||
@@ -138,9 +138,9 @@ async def get_environment_databases(
|
||||
raise HTTPException(status_code=404, detail="Environment not found")
|
||||
|
||||
try:
|
||||
# Initialize SupersetClient from environment config
|
||||
client = SupersetClient(env)
|
||||
return client.get_databases_summary()
|
||||
# Initialize AsyncSupersetClient from environment config
|
||||
client = AsyncSupersetClient(env)
|
||||
return await client.get_databases_summary()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to fetch databases: {e!s}")
|
||||
# #endregion get_environment_databases
|
||||
|
||||
@@ -138,7 +138,7 @@ async def lookup_superset_accounts(
|
||||
)
|
||||
try:
|
||||
logger.reason("Executing Superset account lookup")
|
||||
return service.lookup_superset_accounts(
|
||||
return await service.lookup_superset_accounts(
|
||||
current_user=current_user,
|
||||
request=lookup_request,
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ from ...core.config_manager import ConfigManager
|
||||
from ...core.config_models import AppConfig, Environment, GlobalSettings, LoggingConfig
|
||||
from ...core.database import get_db
|
||||
from ...core.logger import belief_scope, logger
|
||||
from ...core.superset_client import SupersetClient
|
||||
from ...core.async_superset_client import AsyncSupersetClient
|
||||
from ...dependencies import get_config_manager, has_permission
|
||||
from ...models.config import AppConfigRecord
|
||||
from ...models.llm import ValidationPolicy
|
||||
@@ -73,12 +73,12 @@ def _normalize_superset_env_url(raw_url: str) -> str:
|
||||
# @BRIEF Run lightweight Superset connectivity validation without full pagination scan.
|
||||
# @PRE env contains valid URL and credentials.
|
||||
# @POST Raises on auth/API failures; returns None on success.
|
||||
def _validate_superset_connection_fast(env: Environment) -> None:
|
||||
client = SupersetClient(env)
|
||||
async def _validate_superset_connection_fast(env: Environment) -> None:
|
||||
client = AsyncSupersetClient(env)
|
||||
# 1) Explicit auth check
|
||||
client.authenticate()
|
||||
await client.authenticate()
|
||||
# 2) Single lightweight API call to ensure read access
|
||||
client.get_dashboards_page(
|
||||
_, _ = await client.get_dashboards_page(
|
||||
query={
|
||||
"page": 0,
|
||||
"page_size": 1,
|
||||
@@ -238,7 +238,7 @@ async def add_environment(
|
||||
|
||||
# Validate connection before adding (fast path)
|
||||
try:
|
||||
_validate_superset_connection_fast(env)
|
||||
await _validate_superset_connection_fast(env)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"[add_environment][Coherence:Failed] Connection validation failed: {e}"
|
||||
@@ -280,7 +280,7 @@ async def update_environment(
|
||||
|
||||
# Validate connection before updating (fast path)
|
||||
try:
|
||||
_validate_superset_connection_fast(env_to_validate)
|
||||
await _validate_superset_connection_fast(env_to_validate)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"[update_environment][Coherence:Failed] Connection validation failed: {e}"
|
||||
@@ -331,7 +331,7 @@ async def test_environment_connection(
|
||||
raise HTTPException(status_code=404, detail=f"Environment {id} not found")
|
||||
|
||||
try:
|
||||
_validate_superset_connection_fast(env)
|
||||
await _validate_superset_connection_fast(env)
|
||||
|
||||
logger.info(
|
||||
f"[test_environment_connection][Coherence:OK] Connection successful for {id}"
|
||||
|
||||
Reference in New Issue
Block a user