Files
ss-tools/backend/src/api/routes/dashboards/_projection.py
busya 83b8f4d999 logs: migrate to molecular CoT JSON protocol, fix regressions
- Replace BeliefFormatter with CotJsonFormatter (single-line JSON with
  ts, level, trace_id, src, marker, intent, payload, error, span_id)
- Migrate belief_scope to use cot_log() for structured JSON output
- Update monkey-patched reason/reflect/explore to pass extra= dict
- Strip 200+ inline [REASON]/[REFLECT]/[EXPLORE] markers from 50+ files
- Remove belief_scope from hot-path utilities (_parse_datetime,
  _json_load_if_needed) to eliminate startup log noise
- Register TraceContextMiddleware in app.py (was implemented but unused)
- Seed trace_id in startup_event/shutdown_event for background tasks
- Fix broken relative imports in translate routes (3 dots → 4 dots)
- Migrate 43 translate_routes log calls to logger.reason/explore
- Fix SyntaxError (positional arg after extra=) in _repo_operations_routes
- Fix IndentationError in rbac_permission_catalog except-block
- Add smoke test tests/test_smoke_app.py (catches import errors before run)
2026-05-13 09:44:50 +03:00

250 lines
9.2 KiB
Python

# #region DashboardProjection [C:2] [TYPE Module] [SEMANTICS api, dashboards, projection, profile, filtering]
# @BRIEF Dashboard response projection and profile-filter helpers for Dashboard Hub routes.
# @LAYER: Infra
# @RELATION DEPENDS_ON -> [SupersetClient]
# @RELATION DEPENDS_ON -> [ProfileService]
from typing import Any, Dict, List, Optional
from src.core.logger import logger, belief_scope
from src.core.superset_client import SupersetClient
from src.core.superset_profile_lookup import SupersetAccountLookupAdapter
from src.models.auth import User
from src.services.profile_service import ProfileService
# #region _normalize_actor_alias_token [C:2] [TYPE Function]
# @BRIEF Normalize actor alias token to comparable trim+lower text.
def _normalize_actor_alias_token(value: Any) -> Optional[str]:
if value is None:
return None
normalized = str(value).strip().lower()
return normalized if normalized else None
# #endregion _normalize_actor_alias_token
# #region _normalize_owner_display_token [C:2] [TYPE Function]
# @BRIEF Project owner payload value into stable display string for API response contracts.
def _normalize_owner_display_token(owner: Any) -> Optional[str]:
if owner is None:
return None
if isinstance(owner, dict):
for key in ("username", "full_name", "first_name", "email"):
candidate = owner.get(key)
if isinstance(candidate, str) and candidate.strip():
return candidate.strip()
return None
if isinstance(owner, str):
return owner.strip() or None
return None
# #endregion _normalize_owner_display_token
# #region _normalize_dashboard_owner_values [C:2] [TYPE Function]
# @BRIEF Normalize dashboard owners payload to optional list of display strings.
def _normalize_dashboard_owner_values(owners: Any) -> Optional[List[str]]:
if owners is None:
return None
raw_items: List[Any]
if isinstance(owners, list):
raw_items = owners
else:
raw_items = [owners]
normalized: List[str] = []
for owner in raw_items:
token = _normalize_owner_display_token(owner)
if token and token not in normalized:
normalized.append(token)
return normalized
# #endregion _normalize_dashboard_owner_values
# #region _project_dashboard_response_items [C:2] [TYPE Function]
# @BRIEF Project dashboard payloads to response-contract-safe shape.
def _project_dashboard_response_items(
dashboards: List[Dict[str, Any]],
) -> List[Dict[str, Any]]:
projected: List[Dict[str, Any]] = []
for dashboard in dashboards:
projected_dashboard = dict(dashboard)
projected_dashboard["owners"] = _normalize_dashboard_owner_values(
projected_dashboard.get("owners")
)
projected.append(projected_dashboard)
return projected
# #endregion _project_dashboard_response_items
# #region _get_profile_filter_binding [C:2] [TYPE Function]
# @BRIEF Resolve dashboard profile-filter binding through current or legacy profile service contracts.
def _get_profile_filter_binding(
profile_service: Any, current_user: User
) -> Dict[str, Any]:
def _read_optional_string(value: Any) -> Optional[str]:
return value if isinstance(value, str) else None
def _read_bool(value: Any, default: bool) -> bool:
return value if isinstance(value, bool) else default
if hasattr(profile_service, "get_dashboard_filter_binding"):
binding = profile_service.get_dashboard_filter_binding(current_user)
if isinstance(binding, dict):
return {
"superset_username": _read_optional_string(
binding.get("superset_username")
),
"superset_username_normalized": _read_optional_string(
binding.get("superset_username_normalized")
),
"show_only_my_dashboards": _read_bool(
binding.get("show_only_my_dashboards"), False
),
"show_only_slug_dashboards": _read_bool(
binding.get("show_only_slug_dashboards"), False
),
}
if hasattr(profile_service, "get_my_preference"):
response = profile_service.get_my_preference(current_user)
preference = getattr(response, "preference", None)
return {
"superset_username": _read_optional_string(
getattr(preference, "superset_username", None)
),
"superset_username_normalized": _read_optional_string(
getattr(preference, "superset_username_normalized", None)
),
"show_only_my_dashboards": _read_bool(
getattr(preference, "show_only_my_dashboards", False), False
),
"show_only_slug_dashboards": _read_bool(
getattr(preference, "show_only_slug_dashboards", False), False
),
}
return {
"superset_username": None,
"superset_username_normalized": None,
"show_only_my_dashboards": False,
"show_only_slug_dashboards": False,
}
# #endregion _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]:
normalized_bound = _normalize_actor_alias_token(bound_username)
if not normalized_bound:
return []
aliases: List[str] = [normalized_bound]
try:
client = SupersetClient(env)
adapter = SupersetAccountLookupAdapter(
network_client=client.network,
environment_id=str(getattr(env, "id", "")),
)
lookup_payload = adapter.get_users_page(
search=normalized_bound,
page_index=0,
page_size=20,
sort_column="username",
sort_order="asc",
)
lookup_items = (
lookup_payload.get("items", []) if isinstance(lookup_payload, dict) else []
)
matched_item: Optional[Dict[str, Any]] = None
for item in lookup_items:
if not isinstance(item, dict):
continue
if _normalize_actor_alias_token(item.get("username")) == normalized_bound:
matched_item = item
break
if matched_item is None:
for item in lookup_items:
if isinstance(item, dict):
matched_item = item
break
display_alias = _normalize_actor_alias_token(
(matched_item or {}).get("display_name")
)
if display_alias and display_alias not in aliases:
aliases.append(display_alias)
logger.reflect(
"Resolved profile actor aliases",
extra={"src": "_resolve_profile_actor_aliases", "payload": {"env": getattr(env, 'id', None), "bound_username": normalized_bound, "lookup_items": len(lookup_items), "aliases": aliases}},
)
except Exception as alias_error:
logger.explore(
"Failed to resolve profile actor aliases via Superset users lookup",
extra={"src": "_resolve_profile_actor_aliases", "payload": {"env": getattr(env, 'id', None), "bound_username": normalized_bound}, "error": str(alias_error)},
)
return aliases
# #endregion _resolve_profile_actor_aliases
# #region _matches_dashboard_actor_aliases [C:2] [TYPE Function]
# @BRIEF Apply profile actor matching against multiple aliases (username + optional display name).
def _matches_dashboard_actor_aliases(
profile_service: ProfileService,
actor_aliases: List[str],
owners: Optional[Any],
modified_by: Optional[str],
) -> bool:
for actor_alias in actor_aliases:
if profile_service.matches_dashboard_actor(
bound_username=actor_alias,
owners=owners,
modified_by=modified_by,
):
return True
return False
# #endregion _matches_dashboard_actor_aliases
# #region _task_matches_dashboard [C:2] [TYPE Function]
# @BRIEF Checks whether task params are tied to a specific dashboard and environment.
def _task_matches_dashboard(
task: Any, dashboard_id: int, env_id: Optional[str]
) -> bool:
plugin_id = getattr(task, "plugin_id", None)
if plugin_id not in {"superset-backup", "llm_dashboard_validation"}:
return False
params = getattr(task, "params", {}) or {}
dashboard_id_str = str(dashboard_id)
if plugin_id == "llm_dashboard_validation":
task_dashboard_id = params.get("dashboard_id")
if str(task_dashboard_id) != dashboard_id_str:
return False
if env_id:
task_env = params.get("environment_id")
return str(task_env) == str(env_id)
return True
dashboard_ids = params.get("dashboard_ids") or params.get("dashboards") or []
normalized_ids = {str(item) for item in dashboard_ids}
if dashboard_id_str not in normalized_ids:
return False
if env_id:
task_env = params.get("environment_id") or params.get("env")
return str(task_env) == str(env_id)
return True
# #endregion _task_matches_dashboard
# #endregion DashboardProjection