219 lines
8.3 KiB
Python
Executable File
219 lines
8.3 KiB
Python
Executable File
# [DEF:SettingsRouter:Module]
|
|
#
|
|
# @SEMANTICS: settings, api, router, fastapi
|
|
# @PURPOSE: Provides API endpoints for managing application settings and Superset environments.
|
|
# @LAYER: UI (API)
|
|
# @RELATION: DEPENDS_ON -> ConfigManager
|
|
# @RELATION: DEPENDS_ON -> ConfigModels
|
|
#
|
|
# @INVARIANT: All settings changes must be persisted via ConfigManager.
|
|
# @PUBLIC_API: router
|
|
|
|
# [SECTION: IMPORTS]
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from typing import List
|
|
from ...core.config_models import AppConfig, Environment, GlobalSettings
|
|
from ...dependencies import get_config_manager
|
|
from ...core.config_manager import ConfigManager
|
|
from ...core.logger import logger
|
|
from superset_tool.client import SupersetClient
|
|
from superset_tool.models import SupersetConfig
|
|
import os
|
|
# [/SECTION]
|
|
|
|
router = APIRouter()
|
|
|
|
# [DEF:get_settings:Function]
|
|
# @PURPOSE: Retrieves all application settings.
|
|
# @RETURN: AppConfig - The current configuration.
|
|
@router.get("/", response_model=AppConfig)
|
|
async def get_settings(config_manager: ConfigManager = Depends(get_config_manager)):
|
|
logger.info("[get_settings][Entry] Fetching all settings")
|
|
config = config_manager.get_config().copy(deep=True)
|
|
# Mask passwords
|
|
for env in config.environments:
|
|
if env.password:
|
|
env.password = "********"
|
|
return config
|
|
# [/DEF:get_settings]
|
|
|
|
# [DEF:update_global_settings:Function]
|
|
# @PURPOSE: Updates global application settings.
|
|
# @PARAM: settings (GlobalSettings) - The new global settings.
|
|
# @RETURN: GlobalSettings - The updated settings.
|
|
@router.patch("/global", response_model=GlobalSettings)
|
|
async def update_global_settings(
|
|
settings: GlobalSettings,
|
|
config_manager: ConfigManager = Depends(get_config_manager)
|
|
):
|
|
logger.info("[update_global_settings][Entry] Updating global settings")
|
|
config_manager.update_global_settings(settings)
|
|
return settings
|
|
# [/DEF:update_global_settings]
|
|
|
|
# [DEF:get_environments:Function]
|
|
# @PURPOSE: Lists all configured Superset environments.
|
|
# @RETURN: List[Environment] - List of environments.
|
|
@router.get("/environments", response_model=List[Environment])
|
|
async def get_environments(config_manager: ConfigManager = Depends(get_config_manager)):
|
|
logger.info("[get_environments][Entry] Fetching environments")
|
|
return config_manager.get_environments()
|
|
# [/DEF:get_environments]
|
|
|
|
# [DEF:add_environment:Function]
|
|
# @PURPOSE: Adds a new Superset environment.
|
|
# @PARAM: env (Environment) - The environment to add.
|
|
# @RETURN: Environment - The added environment.
|
|
@router.post("/environments", response_model=Environment)
|
|
async def add_environment(
|
|
env: Environment,
|
|
config_manager: ConfigManager = Depends(get_config_manager)
|
|
):
|
|
logger.info(f"[add_environment][Entry] Adding environment {env.id}")
|
|
|
|
# Validate connection before adding
|
|
try:
|
|
superset_config = SupersetConfig(
|
|
env=env.name,
|
|
base_url=env.url,
|
|
auth={
|
|
"provider": "db",
|
|
"username": env.username,
|
|
"password": env.password,
|
|
"refresh": "true"
|
|
}
|
|
)
|
|
client = SupersetClient(config=superset_config)
|
|
client.get_dashboards(query={"page_size": 1})
|
|
except Exception as e:
|
|
logger.error(f"[add_environment][Coherence:Failed] Connection validation failed: {e}")
|
|
raise HTTPException(status_code=400, detail=f"Connection validation failed: {e}")
|
|
|
|
config_manager.add_environment(env)
|
|
return env
|
|
# [/DEF:add_environment]
|
|
|
|
# [DEF:update_environment:Function]
|
|
# @PURPOSE: Updates an existing Superset environment.
|
|
# @PARAM: id (str) - The ID of the environment to update.
|
|
# @PARAM: env (Environment) - The updated environment data.
|
|
# @RETURN: Environment - The updated environment.
|
|
@router.put("/environments/{id}", response_model=Environment)
|
|
async def update_environment(
|
|
id: str,
|
|
env: Environment,
|
|
config_manager: ConfigManager = Depends(get_config_manager)
|
|
):
|
|
logger.info(f"[update_environment][Entry] Updating environment {id}")
|
|
|
|
# If password is masked, we need the real one for validation
|
|
env_to_validate = env.copy(deep=True)
|
|
if env_to_validate.password == "********":
|
|
old_env = next((e for e in config_manager.get_environments() if e.id == id), None)
|
|
if old_env:
|
|
env_to_validate.password = old_env.password
|
|
|
|
# Validate connection before updating
|
|
try:
|
|
superset_config = SupersetConfig(
|
|
env=env_to_validate.name,
|
|
base_url=env_to_validate.url,
|
|
auth={
|
|
"provider": "db",
|
|
"username": env_to_validate.username,
|
|
"password": env_to_validate.password,
|
|
"refresh": "true"
|
|
}
|
|
)
|
|
client = SupersetClient(config=superset_config)
|
|
client.get_dashboards(query={"page_size": 1})
|
|
except Exception as e:
|
|
logger.error(f"[update_environment][Coherence:Failed] Connection validation failed: {e}")
|
|
raise HTTPException(status_code=400, detail=f"Connection validation failed: {e}")
|
|
|
|
if config_manager.update_environment(id, env):
|
|
return env
|
|
raise HTTPException(status_code=404, detail=f"Environment {id} not found")
|
|
# [/DEF:update_environment]
|
|
|
|
# [DEF:delete_environment:Function]
|
|
# @PURPOSE: Deletes a Superset environment.
|
|
# @PARAM: id (str) - The ID of the environment to delete.
|
|
@router.delete("/environments/{id}")
|
|
async def delete_environment(
|
|
id: str,
|
|
config_manager: ConfigManager = Depends(get_config_manager)
|
|
):
|
|
logger.info(f"[delete_environment][Entry] Deleting environment {id}")
|
|
config_manager.delete_environment(id)
|
|
return {"message": f"Environment {id} deleted"}
|
|
# [/DEF:delete_environment]
|
|
|
|
# [DEF:test_environment_connection:Function]
|
|
# @PURPOSE: Tests the connection to a Superset environment.
|
|
# @PARAM: id (str) - The ID of the environment to test.
|
|
# @RETURN: dict - Success message or error.
|
|
@router.post("/environments/{id}/test")
|
|
async def test_environment_connection(
|
|
id: str,
|
|
config_manager: ConfigManager = Depends(get_config_manager)
|
|
):
|
|
logger.info(f"[test_environment_connection][Entry] Testing environment {id}")
|
|
|
|
# Find environment
|
|
env = next((e for e in config_manager.get_environments() if e.id == id), None)
|
|
if not env:
|
|
raise HTTPException(status_code=404, detail=f"Environment {id} not found")
|
|
|
|
try:
|
|
# Create SupersetConfig
|
|
# Note: SupersetConfig expects 'auth' dict with specific keys
|
|
superset_config = SupersetConfig(
|
|
env=env.name,
|
|
base_url=env.url,
|
|
auth={
|
|
"provider": "db", # Defaulting to db for now
|
|
"username": env.username,
|
|
"password": env.password,
|
|
"refresh": "true"
|
|
}
|
|
)
|
|
|
|
# Initialize client (this will trigger authentication)
|
|
client = SupersetClient(config=superset_config)
|
|
|
|
# Try a simple request to verify
|
|
client.get_dashboards(query={"page_size": 1})
|
|
|
|
logger.info(f"[test_environment_connection][Coherence:OK] Connection successful for {id}")
|
|
return {"status": "success", "message": "Connection successful"}
|
|
except Exception as e:
|
|
logger.error(f"[test_environment_connection][Coherence:Failed] Connection failed for {id}: {e}")
|
|
return {"status": "error", "message": str(e)}
|
|
# [/DEF:test_environment_connection]
|
|
|
|
# [DEF:validate_backup_path:Function]
|
|
# @PURPOSE: Validates if a backup path exists and is writable.
|
|
# @PARAM: path (str) - The path to validate.
|
|
# @RETURN: dict - Validation result.
|
|
@router.post("/validate-path")
|
|
async def validate_backup_path(
|
|
path_data: dict,
|
|
config_manager: ConfigManager = Depends(get_config_manager)
|
|
):
|
|
path = path_data.get("path")
|
|
if not path:
|
|
raise HTTPException(status_code=400, detail="Path is required")
|
|
|
|
logger.info(f"[validate_backup_path][Entry] Validating path: {path}")
|
|
|
|
valid, message = config_manager.validate_path(path)
|
|
|
|
if not valid:
|
|
return {"status": "error", "message": message}
|
|
|
|
return {"status": "success", "message": message}
|
|
# [/DEF:validate_backup_path]
|
|
|
|
# [/DEF:SettingsRouter]
|