refactor
This commit is contained in:
@@ -18,7 +18,7 @@ from ...dependencies import get_config_manager, get_task_manager, get_resource_s
|
||||
from ...core.logger import logger, belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(prefix="/api/dashboards", tags=["Dashboards"])
|
||||
|
||||
# [DEF:GitStatus:DataClass]
|
||||
class GitStatus(BaseModel):
|
||||
@@ -65,7 +65,7 @@ class DashboardsResponse(BaseModel):
|
||||
# @PARAM: page_size (Optional[int]) - Items per page (default: 10, max: 100)
|
||||
# @RETURN: DashboardsResponse - List of dashboards with status metadata
|
||||
# @RELATION: CALLS -> ResourceService.get_dashboards_with_status
|
||||
@router.get("/api/dashboards", response_model=DashboardsResponse)
|
||||
@router.get("", response_model=DashboardsResponse)
|
||||
async def get_dashboards(
|
||||
env_id: str,
|
||||
search: Optional[str] = None,
|
||||
@@ -157,7 +157,7 @@ class TaskResponse(BaseModel):
|
||||
# @RETURN: TaskResponse - Task ID for tracking
|
||||
# @RELATION: DISPATCHES -> MigrationPlugin
|
||||
# @RELATION: CALLS -> task_manager.create_task
|
||||
@router.post("/api/dashboards/migrate", response_model=TaskResponse)
|
||||
@router.post("/migrate", response_model=TaskResponse)
|
||||
async def migrate_dashboards(
|
||||
request: MigrateRequest,
|
||||
config_manager=Depends(get_config_manager),
|
||||
@@ -225,7 +225,7 @@ class BackupRequest(BaseModel):
|
||||
# @RETURN: TaskResponse - Task ID for tracking
|
||||
# @RELATION: DISPATCHES -> BackupPlugin
|
||||
# @RELATION: CALLS -> task_manager.create_task
|
||||
@router.post("/api/dashboards/backup", response_model=TaskResponse)
|
||||
@router.post("/backup", response_model=TaskResponse)
|
||||
async def backup_dashboards(
|
||||
request: BackupRequest,
|
||||
config_manager=Depends(get_config_manager),
|
||||
@@ -289,7 +289,7 @@ class DatabaseMappingsResponse(BaseModel):
|
||||
# @PARAM: target_env_id (str) - Target environment ID
|
||||
# @RETURN: DatabaseMappingsResponse - List of suggested mappings
|
||||
# @RELATION: CALLS -> MappingService.get_suggestions
|
||||
@router.get("/api/dashboards/db-mappings", response_model=DatabaseMappingsResponse)
|
||||
@router.get("/db-mappings", response_model=DatabaseMappingsResponse)
|
||||
async def get_database_mappings(
|
||||
source_env_id: str,
|
||||
target_env_id: str,
|
||||
|
||||
@@ -19,7 +19,7 @@ from ...core.logger import logger, belief_scope
|
||||
from ...core.superset_client import SupersetClient
|
||||
# [/SECTION]
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(prefix="/api/datasets", tags=["Datasets"])
|
||||
|
||||
# [DEF:MappedFields:DataClass]
|
||||
class MappedFields(BaseModel):
|
||||
@@ -63,8 +63,8 @@ class DatasetColumn(BaseModel):
|
||||
# [DEF:DatasetDetailResponse:DataClass]
|
||||
class DatasetDetailResponse(BaseModel):
|
||||
id: int
|
||||
table_name: str
|
||||
schema: str
|
||||
table_name: Optional[str] = None
|
||||
schema: Optional[str] = None
|
||||
database: str
|
||||
description: Optional[str] = None
|
||||
columns: List[DatasetColumn]
|
||||
@@ -99,7 +99,7 @@ class TaskResponse(BaseModel):
|
||||
# @PARAM: search (Optional[str]) - Filter by table name
|
||||
# @RETURN: List[int] - List of dataset IDs
|
||||
# @RELATION: CALLS -> ResourceService.get_datasets_with_status
|
||||
@router.get("/api/datasets/ids")
|
||||
@router.get("/ids")
|
||||
async def get_dataset_ids(
|
||||
env_id: str,
|
||||
search: Optional[str] = None,
|
||||
@@ -155,7 +155,7 @@ async def get_dataset_ids(
|
||||
# @PARAM: page_size (Optional[int]) - Items per page (default: 10, max: 100)
|
||||
# @RETURN: DatasetsResponse - List of datasets with status metadata
|
||||
# @RELATION: CALLS -> ResourceService.get_datasets_with_status
|
||||
@router.get("/api/datasets", response_model=DatasetsResponse)
|
||||
@router.get("", response_model=DatasetsResponse)
|
||||
async def get_datasets(
|
||||
env_id: str,
|
||||
search: Optional[str] = None,
|
||||
@@ -241,7 +241,7 @@ class MapColumnsRequest(BaseModel):
|
||||
# @RETURN: TaskResponse - Task ID for tracking
|
||||
# @RELATION: DISPATCHES -> MapperPlugin
|
||||
# @RELATION: CALLS -> task_manager.create_task
|
||||
@router.post("/api/datasets/map-columns", response_model=TaskResponse)
|
||||
@router.post("/map-columns", response_model=TaskResponse)
|
||||
async def map_columns(
|
||||
request: MapColumnsRequest,
|
||||
config_manager=Depends(get_config_manager),
|
||||
@@ -310,7 +310,7 @@ class GenerateDocsRequest(BaseModel):
|
||||
# @RETURN: TaskResponse - Task ID for tracking
|
||||
# @RELATION: DISPATCHES -> LLMAnalysisPlugin
|
||||
# @RELATION: CALLS -> task_manager.create_task
|
||||
@router.post("/api/datasets/generate-docs", response_model=TaskResponse)
|
||||
@router.post("/generate-docs", response_model=TaskResponse)
|
||||
async def generate_docs(
|
||||
request: GenerateDocsRequest,
|
||||
config_manager=Depends(get_config_manager),
|
||||
@@ -363,7 +363,7 @@ async def generate_docs(
|
||||
# @PARAM: dataset_id (int) - The dataset ID
|
||||
# @RETURN: DatasetDetailResponse - Detailed dataset information
|
||||
# @RELATION: CALLS -> SupersetClient.get_dataset_detail
|
||||
@router.get("/api/datasets/{dataset_id}", response_model=DatasetDetailResponse)
|
||||
@router.get("/{dataset_id}", response_model=DatasetDetailResponse)
|
||||
async def get_dataset_detail(
|
||||
env_id: str,
|
||||
dataset_id: int,
|
||||
|
||||
@@ -18,7 +18,7 @@ from pydantic import BaseModel, Field
|
||||
from ...core.logger import belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(prefix="/api/environments", tags=["Environments"])
|
||||
|
||||
# [DEF:ScheduleSchema:DataClass]
|
||||
class ScheduleSchema(BaseModel):
|
||||
@@ -43,6 +43,8 @@ class DatabaseResponse(BaseModel):
|
||||
|
||||
# [DEF:get_environments:Function]
|
||||
# @PURPOSE: List all configured environments.
|
||||
# @LAYER: API
|
||||
# @SEMANTICS: list, environments, config
|
||||
# @PRE: config_manager is injected via Depends.
|
||||
# @POST: Returns a list of EnvironmentResponse objects.
|
||||
# @RETURN: List[EnvironmentResponse]
|
||||
@@ -71,6 +73,8 @@ async def get_environments(
|
||||
|
||||
# [DEF:update_environment_schedule:Function]
|
||||
# @PURPOSE: Update backup schedule for an environment.
|
||||
# @LAYER: API
|
||||
# @SEMANTICS: update, schedule, backup, environment
|
||||
# @PRE: Environment id exists, schedule is valid ScheduleSchema.
|
||||
# @POST: Backup schedule updated and scheduler reloaded.
|
||||
# @PARAM: id (str) - The environment ID.
|
||||
@@ -103,6 +107,8 @@ async def update_environment_schedule(
|
||||
|
||||
# [DEF:get_environment_databases:Function]
|
||||
# @PURPOSE: Fetch the list of databases from a specific environment.
|
||||
# @LAYER: API
|
||||
# @SEMANTICS: fetch, databases, superset, environment
|
||||
# @PRE: Environment id exists.
|
||||
# @POST: Returns a list of database summaries from the environment.
|
||||
# @PARAM: id (str) - The environment ID.
|
||||
|
||||
@@ -26,7 +26,7 @@ from src.api.routes.git_schemas import (
|
||||
from src.services.git_service import GitService
|
||||
from src.core.logger import logger, belief_scope
|
||||
|
||||
router = APIRouter(prefix="/api/git", tags=["git"])
|
||||
router = APIRouter(tags=["git"])
|
||||
git_service = GitService()
|
||||
|
||||
# [DEF:get_git_configs:Function]
|
||||
|
||||
@@ -16,7 +16,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
# [DEF:router:Global]
|
||||
# @PURPOSE: APIRouter instance for LLM routes.
|
||||
router = APIRouter(prefix="/api/llm", tags=["LLM"])
|
||||
router = APIRouter(tags=["LLM"])
|
||||
# [/DEF:router:Global]
|
||||
|
||||
# [DEF:get_providers:Function]
|
||||
|
||||
@@ -21,7 +21,7 @@ from ...models.mapping import DatabaseMapping
|
||||
from pydantic import BaseModel
|
||||
# [/SECTION]
|
||||
|
||||
router = APIRouter(prefix="/api/mappings", tags=["mappings"])
|
||||
router = APIRouter(tags=["mappings"])
|
||||
|
||||
# [DEF:MappingCreate:DataClass]
|
||||
class MappingCreate(BaseModel):
|
||||
|
||||
@@ -44,7 +44,7 @@ async def get_dashboards(
|
||||
# @POST: Starts the migration task and returns the task ID.
|
||||
# @PARAM: selection (DashboardSelection) - The dashboards to migrate.
|
||||
# @RETURN: Dict - {"task_id": str, "message": str}
|
||||
@router.post("/migration/execute")
|
||||
@router.post("/execute")
|
||||
async def execute_migration(
|
||||
selection: DashboardSelection,
|
||||
config_manager=Depends(get_config_manager),
|
||||
|
||||
@@ -283,6 +283,7 @@ class ConsolidatedSettingsResponse(BaseModel):
|
||||
environments: List[dict]
|
||||
connections: List[dict]
|
||||
llm: dict
|
||||
llm_providers: List[dict]
|
||||
logging: dict
|
||||
storage: dict
|
||||
# [/DEF:ConsolidatedSettingsResponse:Class]
|
||||
@@ -302,13 +303,74 @@ async def get_consolidated_settings(
|
||||
|
||||
config = config_manager.get_config()
|
||||
|
||||
from ...services.llm_provider import LLMProviderService
|
||||
from ...core.database import SessionLocal
|
||||
db = SessionLocal()
|
||||
try:
|
||||
llm_service = LLMProviderService(db)
|
||||
providers = llm_service.get_all_providers()
|
||||
llm_providers_list = [
|
||||
{
|
||||
"id": p.id,
|
||||
"provider_type": p.provider_type,
|
||||
"name": p.name,
|
||||
"base_url": p.base_url,
|
||||
"api_key": "********",
|
||||
"default_model": p.default_model,
|
||||
"is_active": p.is_active
|
||||
} for p in providers
|
||||
]
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return ConsolidatedSettingsResponse(
|
||||
environments=[env.dict() for env in config.environments],
|
||||
connections=config.settings.connections,
|
||||
llm=config.settings.llm,
|
||||
llm_providers=llm_providers_list,
|
||||
logging=config.settings.logging.dict(),
|
||||
storage=config.settings.storage.dict()
|
||||
)
|
||||
# [/DEF:get_consolidated_settings:Function]
|
||||
|
||||
# [DEF:update_consolidated_settings:Function]
|
||||
# @PURPOSE: Bulk update application settings from the consolidated view.
|
||||
# @PRE: User has admin permissions, config is valid.
|
||||
# @POST: Settings are updated and saved via ConfigManager.
|
||||
@router.patch("/consolidated")
|
||||
async def update_consolidated_settings(
|
||||
settings_patch: dict,
|
||||
config_manager: ConfigManager = Depends(get_config_manager),
|
||||
_ = Depends(has_permission("admin:settings", "WRITE"))
|
||||
):
|
||||
with belief_scope("update_consolidated_settings"):
|
||||
logger.info("[update_consolidated_settings][Entry] Applying consolidated settings patch")
|
||||
|
||||
current_config = config_manager.get_config()
|
||||
current_settings = current_config.settings
|
||||
|
||||
# Update connections if provided
|
||||
if "connections" in settings_patch:
|
||||
current_settings.connections = settings_patch["connections"]
|
||||
|
||||
# Update LLM if provided
|
||||
if "llm" in settings_patch:
|
||||
current_settings.llm = settings_patch["llm"]
|
||||
|
||||
# Update Logging if provided
|
||||
if "logging" in settings_patch:
|
||||
current_settings.logging = LoggingConfig(**settings_patch["logging"])
|
||||
|
||||
# Update Storage if provided
|
||||
if "storage" in settings_patch:
|
||||
new_storage = StorageConfig(**settings_patch["storage"])
|
||||
is_valid, message = config_manager.validate_path(new_storage.root_path)
|
||||
if not is_valid:
|
||||
raise HTTPException(status_code=400, detail=message)
|
||||
current_settings.storage = new_storage
|
||||
|
||||
config_manager.update_global_settings(current_settings)
|
||||
return {"status": "success", "message": "Settings updated"}
|
||||
# [/DEF:update_consolidated_settings:Function]
|
||||
|
||||
# [/DEF:SettingsRouter:Module]
|
||||
|
||||
@@ -115,14 +115,21 @@ app.include_router(plugins.router, prefix="/api/plugins", tags=["Plugins"])
|
||||
app.include_router(tasks.router, prefix="/api/tasks", tags=["Tasks"])
|
||||
app.include_router(settings.router, prefix="/api/settings", tags=["Settings"])
|
||||
app.include_router(connections.router, prefix="/api/settings/connections", tags=["Connections"])
|
||||
app.include_router(environments.router, prefix="/api/settings/environments", tags=["Environments"])
|
||||
app.include_router(mappings.router)
|
||||
app.include_router(environments.router, tags=["Environments"])
|
||||
app.include_router(mappings.router, prefix="/api/mappings", tags=["Mappings"])
|
||||
app.include_router(migration.router)
|
||||
app.include_router(git.router)
|
||||
app.include_router(llm.router)
|
||||
app.include_router(git.router, prefix="/api/git", tags=["Git"])
|
||||
app.include_router(llm.router, prefix="/api/llm", tags=["LLM"])
|
||||
app.include_router(storage.router, prefix="/api/storage", tags=["Storage"])
|
||||
app.include_router(dashboards.router, tags=["Dashboards"])
|
||||
app.include_router(datasets.router, tags=["Datasets"])
|
||||
app.include_router(dashboards.router)
|
||||
app.include_router(datasets.router)
|
||||
|
||||
|
||||
# [DEF:api.include_routers:Action]
|
||||
# @PURPOSE: Registers all API routers with the FastAPI application.
|
||||
# @LAYER: API
|
||||
# @SEMANTICS: routes, registration, api
|
||||
# [/DEF:api.include_routers:Action]
|
||||
|
||||
# [DEF:websocket_endpoint:Function]
|
||||
# @PURPOSE: Provides a WebSocket endpoint for real-time log streaming of a task with server-side filtering.
|
||||
@@ -233,25 +240,20 @@ async def websocket_endpoint(
|
||||
frontend_path = project_root / "frontend" / "build"
|
||||
if frontend_path.exists():
|
||||
app.mount("/_app", StaticFiles(directory=str(frontend_path / "_app")), name="static")
|
||||
|
||||
# Serve other static files from the root of build directory
|
||||
# [DEF:serve_spa:Function]
|
||||
# @PURPOSE: Serves frontend static files or index.html for SPA routing.
|
||||
# @PRE: file_path is requested by the client.
|
||||
# @POST: Returns the requested file or index.html as a fallback.
|
||||
@app.get("/{file_path:path}")
|
||||
|
||||
@app.get("/{file_path:path}", include_in_schema=False)
|
||||
async def serve_spa(file_path: str):
|
||||
with belief_scope("serve_spa", f"path={file_path}"):
|
||||
# Don't serve SPA for API routes that fell through
|
||||
if file_path.startswith("api/"):
|
||||
logger.info(f"[DEBUG] API route fell through to serve_spa: {file_path}")
|
||||
raise HTTPException(status_code=404, detail=f"API endpoint not found: {file_path}")
|
||||
|
||||
full_path = frontend_path / file_path
|
||||
if full_path.is_file():
|
||||
return FileResponse(str(full_path))
|
||||
# Fallback to index.html for SPA routing
|
||||
return FileResponse(str(frontend_path / "index.html"))
|
||||
# Only serve SPA for non-API paths
|
||||
# API routes are registered separately and should be matched by FastAPI first
|
||||
if file_path and (file_path.startswith("api/") or file_path.startswith("/api/") or file_path == "api"):
|
||||
# This should not happen if API routers are properly registered
|
||||
# Return 404 instead of serving HTML
|
||||
raise HTTPException(status_code=404, detail=f"API endpoint not found: {file_path}")
|
||||
|
||||
full_path = frontend_path / file_path
|
||||
if file_path and full_path.is_file():
|
||||
return FileResponse(str(full_path))
|
||||
return FileResponse(str(frontend_path / "index.html"))
|
||||
# [/DEF:serve_spa:Function]
|
||||
else:
|
||||
# [DEF:read_root:Function]
|
||||
|
||||
@@ -247,8 +247,14 @@ class SupersetClient:
|
||||
def get_dataset_detail(self, dataset_id: int) -> Dict:
|
||||
with belief_scope("SupersetClient.get_dataset_detail", f"id={dataset_id}"):
|
||||
# Get base dataset info
|
||||
dataset = self.get_dataset(dataset_id)
|
||||
response = self.get_dataset(dataset_id)
|
||||
|
||||
# If the response is a dict and has a 'result' key, use that (standard Superset API)
|
||||
if isinstance(response, dict) and 'result' in response:
|
||||
dataset = response['result']
|
||||
else:
|
||||
dataset = response
|
||||
|
||||
# Extract columns information
|
||||
columns = dataset.get("columns", [])
|
||||
column_info = []
|
||||
|
||||
BIN
backend/tasks.db
BIN
backend/tasks.db
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
# [DEF:backend.tests.test_dashboards_api:Module]
|
||||
# @TIER: STANDARD
|
||||
# @PURPOSE: Contract-driven tests for Dashboard Hub API
|
||||
# @LAYER: Domain (Tests)
|
||||
# @SEMANTICS: tests, dashboards, api, contract
|
||||
# @RELATION: TESTS -> backend.src.api.routes.dashboards
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
@@ -48,6 +50,8 @@ def test_get_dashboards_success():
|
||||
# Validate against Pydantic model
|
||||
DashboardsResponse(**data)
|
||||
|
||||
# [/DEF:test_get_dashboards_success:Function]
|
||||
|
||||
# [DEF:test_get_dashboards_env_not_found:Function]
|
||||
# @TEST: GET /api/dashboards returns 404 if env_id missing
|
||||
# @PRE: env_id does not exist
|
||||
@@ -64,4 +68,6 @@ def test_get_dashboards_env_not_found():
|
||||
assert response.status_code == 404
|
||||
assert "Environment not found" in response.json()["detail"]
|
||||
|
||||
# [/DEF:test_get_dashboards_env_not_found:Function]
|
||||
|
||||
# [/DEF:backend.tests.test_dashboards_api:Module]
|
||||
|
||||
@@ -44,4 +44,6 @@ async def test_get_dashboards_with_status():
|
||||
assert result[0]["last_task"]["task_id"] == "task-123"
|
||||
assert result[0]["last_task"]["status"] == "RUNNING"
|
||||
|
||||
# [/DEF:test_get_dashboards_with_status:Function]
|
||||
|
||||
# [/DEF:backend.tests.test_resource_service:Module]
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
<!-- [DEF:frontend.src.routes.+layout:Module] -->
|
||||
<!--
|
||||
@TIER: STANDARD
|
||||
@SEMANTICS: layout, root, navigation, sidebar, toast
|
||||
@PURPOSE: Root layout component that provides global UI structure (Sidebar, Navbar, Footer, TaskDrawer, Toasts).
|
||||
@LAYER: UI (Layout)
|
||||
@RELATION: DEPENDS_ON -> Sidebar
|
||||
@RELATION: DEPENDS_ON -> TopNavbar
|
||||
@RELATION: DEPENDS_ON -> Footer
|
||||
@RELATION: DEPENDS_ON -> Toast
|
||||
@RELATION: DEPENDS_ON -> ProtectedRoute
|
||||
@RELATION: DEPENDS_ON -> TaskDrawer
|
||||
@INVARIANT: All pages except /login are wrapped in ProtectedRoute.
|
||||
-->
|
||||
|
||||
<!-- [DEF:layout:Module] -->
|
||||
<script>
|
||||
import '../app.css';
|
||||
@@ -52,3 +67,4 @@
|
||||
{/if}
|
||||
</main>
|
||||
<!-- [/DEF:layout:Module] -->
|
||||
<!-- [/DEF:frontend.src.routes.+layout:Module] -->
|
||||
|
||||
@@ -49,8 +49,9 @@
|
||||
let showMapColumnsModal = false;
|
||||
let showGenerateDocsModal = false;
|
||||
let mapSourceType = 'postgresql';
|
||||
let mapConnectionId = null;
|
||||
let mapConnectionId = '';
|
||||
let mapFileData = null;
|
||||
let mapFileInput;
|
||||
let llmProvider = '';
|
||||
let llmOptions = {};
|
||||
|
||||
@@ -239,15 +240,42 @@
|
||||
|
||||
// Handle bulk map columns
|
||||
async function handleBulkMapColumns() {
|
||||
if (selectedIds.size === 0) return;
|
||||
console.log('[DatasetHub][handleBulkMapColumns][Entry]', {
|
||||
selectedIds: Array.from(selectedIds),
|
||||
mapSourceType,
|
||||
mapConnectionId,
|
||||
mapFileData
|
||||
});
|
||||
|
||||
if (selectedIds.size === 0) {
|
||||
console.log('[DatasetHub][handleBulkMapColumns] No datasets selected');
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapSourceType === 'postgresql' && !mapConnectionId) {
|
||||
console.log('[DatasetHub][handleBulkMapColumns] No connection ID provided for PostgreSQL');
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapSourceType === 'xlsx' && (!mapFileData || mapFileData.length === 0)) {
|
||||
console.log('[DatasetHub][handleBulkMapColumns] No file selected for XLSX');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let fileData = null;
|
||||
if (mapSourceType === 'xlsx' && mapFileData && mapFileData.length > 0) {
|
||||
// For now we send the filename as a placeholder or handle upload if needed.
|
||||
// The backend expects a string 'file_data' in the current schema.
|
||||
fileData = mapFileData[0].name;
|
||||
}
|
||||
|
||||
const response = await api.postApi('/datasets/map-columns', {
|
||||
env_id: selectedEnv,
|
||||
dataset_ids: Array.from(selectedIds),
|
||||
source_type: mapSourceType,
|
||||
connection_id: mapConnectionId || undefined,
|
||||
file_data: mapFileData || undefined
|
||||
file_data: fileData || undefined
|
||||
});
|
||||
console.log('[DatasetHub][Action] Bulk map columns task created:', response.task_id);
|
||||
|
||||
@@ -808,9 +836,9 @@
|
||||
{#if mapSourceType === 'postgresql'}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Connection ID</label>
|
||||
<input
|
||||
type="text"
|
||||
class="search-input w-full"
|
||||
<input
|
||||
type="text"
|
||||
class="search-input w-full"
|
||||
placeholder="Enter connection ID..."
|
||||
bind:value={mapConnectionId}
|
||||
/>
|
||||
@@ -818,11 +846,12 @@
|
||||
{:else}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">XLSX File</label>
|
||||
<input
|
||||
type="file"
|
||||
<input
|
||||
type="file"
|
||||
class="w-full"
|
||||
accept=".xlsx,.xls"
|
||||
bind:files={mapFileData}
|
||||
bind:this={mapFileInput}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -842,10 +871,11 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="action-btn" on:click={() => showMapColumnsModal = false}>Cancel</button>
|
||||
<button
|
||||
class="action-btn primary"
|
||||
on:click={handleBulkMapColumns}
|
||||
disabled={selectedIds.size === 0}
|
||||
<button
|
||||
type="button"
|
||||
class="action-btn primary"
|
||||
on:click|preventDefault={handleBulkMapColumns}
|
||||
disabled={selectedIds.size === 0 || (mapSourceType === 'postgresql' && !mapConnectionId) || (mapSourceType === 'xlsx' && (!mapFileData || mapFileData.length === 0))}
|
||||
>
|
||||
Start Mapping
|
||||
</button>
|
||||
|
||||
@@ -18,12 +18,29 @@
|
||||
import { t } from '$lib/i18n';
|
||||
import { api } from '$lib/api.js';
|
||||
import { addToast } from '$lib/toasts';
|
||||
import ProviderConfig from '../../components/llm/ProviderConfig.svelte';
|
||||
|
||||
// State
|
||||
let activeTab = 'environments';
|
||||
let settings = null;
|
||||
let isLoading = true;
|
||||
let error = null;
|
||||
|
||||
// Environment editing state
|
||||
let editingEnvId = null;
|
||||
let isAddingEnv = false;
|
||||
let newEnv = {
|
||||
id: '',
|
||||
name: '',
|
||||
url: '',
|
||||
username: '',
|
||||
password: '',
|
||||
is_default: false,
|
||||
backup_schedule: {
|
||||
enabled: false,
|
||||
cron_expression: '0 0 * * *'
|
||||
}
|
||||
};
|
||||
|
||||
// Load settings on mount
|
||||
onMount(async () => {
|
||||
@@ -57,10 +74,13 @@
|
||||
: 'text-gray-600 hover:text-gray-800 border-transparent hover:border-gray-300';
|
||||
}
|
||||
|
||||
// Handle save
|
||||
// Handle global settings save (Logging, Storage)
|
||||
async function handleSave() {
|
||||
console.log('[SettingsPage][Action] Saving settings');
|
||||
try {
|
||||
// In a real app we might want to only send the changed section,
|
||||
// but updateConsolidatedSettings expects full object or we can use specific endpoints.
|
||||
// For now we use the consolidated update.
|
||||
await api.updateConsolidatedSettings(settings);
|
||||
addToast($t.settings?.save_success || 'Settings saved', 'success');
|
||||
} catch (err) {
|
||||
@@ -69,21 +89,89 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder functions for environment actions
|
||||
function handleTestEnv(id) {
|
||||
// Handle environment actions
|
||||
async function handleTestEnv(id) {
|
||||
console.log(`[SettingsPage][Action] Test environment ${id}`);
|
||||
addToast('Environment test started', 'info');
|
||||
addToast('Testing connection...', 'info');
|
||||
try {
|
||||
const result = await api.testEnvironmentConnection(id);
|
||||
if (result.status === 'success') {
|
||||
addToast('Connection successful', 'success');
|
||||
} else {
|
||||
addToast(`Connection failed: ${result.message}`, 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[SettingsPage][Coherence:Failed] Error testing connection:', err);
|
||||
addToast('Failed to test connection', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function editEnv(env) {
|
||||
console.log(`[SettingsPage][Action] Edit environment ${env.id}`);
|
||||
// TODO: Open edit modal
|
||||
newEnv = JSON.parse(JSON.stringify(env)); // Deep copy
|
||||
// Ensure backup_schedule exists
|
||||
if (!newEnv.backup_schedule) {
|
||||
newEnv.backup_schedule = { enabled: false, cron_expression: '0 0 * * *' };
|
||||
}
|
||||
editingEnvId = env.id;
|
||||
isAddingEnv = false;
|
||||
}
|
||||
|
||||
function handleDeleteEnv(id) {
|
||||
function resetEnvForm() {
|
||||
newEnv = {
|
||||
id: '',
|
||||
name: '',
|
||||
url: '',
|
||||
username: '',
|
||||
password: '',
|
||||
is_default: false,
|
||||
backup_schedule: {
|
||||
enabled: false,
|
||||
cron_expression: '0 0 * * *'
|
||||
}
|
||||
};
|
||||
editingEnvId = null;
|
||||
}
|
||||
|
||||
async function handleAddOrUpdateEnv() {
|
||||
try {
|
||||
console.log(`[SettingsPage][Action] ${editingEnvId ? 'Updating' : 'Adding'} environment.`);
|
||||
|
||||
// Basic validation
|
||||
if (!newEnv.id || !newEnv.name || !newEnv.url) {
|
||||
addToast('Please fill in all required fields (ID, Name, URL)', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (editingEnvId) {
|
||||
await api.updateEnvironment(editingEnvId, newEnv);
|
||||
addToast('Environment updated', 'success');
|
||||
} else {
|
||||
await api.addEnvironment(newEnv);
|
||||
addToast('Environment added', 'success');
|
||||
}
|
||||
|
||||
resetEnvForm();
|
||||
editingEnvId = null;
|
||||
isAddingEnv = false;
|
||||
await loadSettings();
|
||||
} catch (error) {
|
||||
console.error("[SettingsPage][Coherence:Failed] Failed to save environment:", error);
|
||||
addToast(error.message || 'Failed to save environment', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteEnv(id) {
|
||||
if (confirm('Are you sure you want to delete this environment?')) {
|
||||
console.log(`[SettingsPage][Action] Delete environment ${id}`);
|
||||
// TODO: Call API to delete
|
||||
try {
|
||||
await api.deleteEnvironment(id);
|
||||
addToast('Environment deleted', 'success');
|
||||
await loadSettings();
|
||||
} catch (error) {
|
||||
console.error("[SettingsPage][Coherence:Failed] Failed to delete environment:", error);
|
||||
addToast('Failed to delete environment', 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -167,6 +255,12 @@
|
||||
>
|
||||
{$t.settings?.environments || 'Environments'}
|
||||
</button>
|
||||
<button
|
||||
class="tab-btn {getTabClass('logging')}"
|
||||
on:click={() => handleTabChange('logging')}
|
||||
>
|
||||
{$t.settings?.logging || 'Logging'}
|
||||
</button>
|
||||
<button
|
||||
class="tab-btn {getTabClass('connections')}"
|
||||
on:click={() => handleTabChange('connections')}
|
||||
@@ -179,12 +273,6 @@
|
||||
>
|
||||
{$t.settings?.llm || 'LLM'}
|
||||
</button>
|
||||
<button
|
||||
class="tab-btn {getTabClass('logging')}"
|
||||
on:click={() => handleTabChange('logging')}
|
||||
>
|
||||
{$t.settings?.logging || 'Logging'}
|
||||
</button>
|
||||
<button
|
||||
class="tab-btn {getTabClass('storage')}"
|
||||
on:click={() => handleTabChange('storage')}
|
||||
@@ -202,13 +290,87 @@
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.env_description || 'Configure Superset environments for dashboards and datasets.'}
|
||||
</p>
|
||||
<div class="flex justify-end mb-6">
|
||||
<button class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700">
|
||||
{$t.settings?.env_add || 'Add Environment'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if !editingEnvId && !isAddingEnv}
|
||||
<div class="flex justify-end mb-6">
|
||||
<button
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
on:click={() => { isAddingEnv = true; resetEnvForm(); }}
|
||||
>
|
||||
{$t.settings?.env_add || 'Add Environment'}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if editingEnvId || isAddingEnv}
|
||||
<!-- Add/Edit Environment Form -->
|
||||
<div class="bg-gray-50 p-6 rounded-lg mb-6 border border-gray-200">
|
||||
<h3 class="text-lg font-medium mb-4">{editingEnvId ? 'Edit' : 'Add'} Environment</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="env_id" class="block text-sm font-medium text-gray-700">ID</label>
|
||||
<input
|
||||
type="text"
|
||||
id="env_id"
|
||||
bind:value={newEnv.id}
|
||||
disabled={!!editingEnvId}
|
||||
class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2 disabled:bg-gray-100 disabled:text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_name" class="block text-sm font-medium text-gray-700">Name</label>
|
||||
<input type="text" id="env_name" bind:value={newEnv.name} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_url" class="block text-sm font-medium text-gray-700">URL</label>
|
||||
<input type="text" id="env_url" bind:value={newEnv.url} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_user" class="block text-sm font-medium text-gray-700">Username</label>
|
||||
<input type="text" id="env_user" bind:value={newEnv.username} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_pass" class="block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" id="env_pass" bind:value={newEnv.password} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div class="flex items-center mt-6">
|
||||
<input type="checkbox" id="env_default" bind:checked={newEnv.is_default} class="h-4 w-4 text-blue-600 border-gray-300 rounded" />
|
||||
<label for="env_default" class="ml-2 block text-sm text-gray-900">Default Environment</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="text-lg font-medium mb-4 mt-6">Backup Schedule</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" id="backup_enabled" bind:checked={newEnv.backup_schedule.enabled} class="h-4 w-4 text-blue-600 border-gray-300 rounded" />
|
||||
<label for="backup_enabled" class="ml-2 block text-sm text-gray-900">Enable Automatic Backups</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="cron_expression" class="block text-sm font-medium text-gray-700">Cron Expression</label>
|
||||
<input type="text" id="cron_expression" bind:value={newEnv.backup_schedule.cron_expression} placeholder="0 0 * * *" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
<p class="text-xs text-gray-500 mt-1">Example: 0 0 * * * (daily at midnight)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex gap-2 justify-end">
|
||||
<button
|
||||
on:click={() => { isAddingEnv = false; editingEnvId = null; resetEnvForm(); }}
|
||||
class="bg-gray-200 text-gray-700 px-4 py-2 rounded hover:bg-gray-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
on:click={handleAddOrUpdateEnv}
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>
|
||||
{editingEnvId ? 'Update' : 'Add'} Environment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if settings.environments && settings.environments.length > 0}
|
||||
<div class="mt-6">
|
||||
<div class="mt-6 overflow-x-auto border border-gray-200 rounded-lg">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
@@ -225,13 +387,21 @@
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.name}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.url}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.username}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.is_default ? 'Yes' : 'No'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if env.is_default}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
|
||||
Yes
|
||||
</span>
|
||||
{:else}
|
||||
<span class="text-gray-500">No</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button class="text-green-600 hover:text-green-900 mr-4" on:click={() => handleTestEnv(env.id)}>
|
||||
{$t.settings?.env_test || "Test"}
|
||||
</button>
|
||||
<button class="text-indigo-600 hover:text-indigo-900 mr-4" on:click={() => editEnv(env)}>
|
||||
{$t.common.edit}
|
||||
{$t.common.edit || "Edit"}
|
||||
</button>
|
||||
<button class="text-red-600 hover:text-red-900" on:click={() => handleDeleteEnv(env.id)}>
|
||||
{$t.settings?.env_delete || "Delete"}
|
||||
@@ -242,29 +412,13 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else if !isAddingEnv}
|
||||
<div class="mb-4 p-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700">
|
||||
<p class="font-bold">Warning</p>
|
||||
<p>No Superset environments configured. You must add at least one environment to perform backups or migrations.</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if activeTab === 'connections'}
|
||||
<!-- Connections Tab -->
|
||||
<div class="text-lg font-medium mb-4">
|
||||
<h2 class="text-xl font-bold mb-4">{$t.settings?.connections || 'Database Connections'}</h2>
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.connections_description || 'Configure database connections for data mapping.'}
|
||||
</p>
|
||||
</div>
|
||||
{:else if activeTab === 'llm'}
|
||||
<!-- LLM Tab -->
|
||||
<div class="text-lg font-medium mb-4">
|
||||
<h2 class="text-xl font-bold mb-4">{$t.settings?.llm || 'LLM Providers'}</h2>
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.llm_description || 'Configure LLM providers for dataset documentation.'}
|
||||
</p>
|
||||
<div class="flex justify-end mb-6">
|
||||
<button class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700">
|
||||
{$t.llm?.add_provider || 'Add Provider'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'logging'}
|
||||
<!-- Logging Tab -->
|
||||
<div class="text-lg font-medium mb-4">
|
||||
@@ -272,6 +426,76 @@
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.logging_description || 'Configure logging and task log levels.'}
|
||||
</p>
|
||||
|
||||
<div class="bg-gray-50 p-6 rounded-lg border border-gray-200">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="log_level" class="block text-sm font-medium text-gray-700">Log Level</label>
|
||||
<select id="log_level" bind:value={settings.logging.level} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2">
|
||||
<option value="DEBUG">DEBUG</option>
|
||||
<option value="INFO">INFO</option>
|
||||
<option value="WARNING">WARNING</option>
|
||||
<option value="ERROR">ERROR</option>
|
||||
<option value="CRITICAL">CRITICAL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="task_log_level" class="block text-sm font-medium text-gray-700">Task Log Level</label>
|
||||
<select id="task_log_level" bind:value={settings.logging.task_log_level} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2">
|
||||
<option value="DEBUG">DEBUG</option>
|
||||
<option value="INFO">INFO</option>
|
||||
<option value="WARNING">WARNING</option>
|
||||
<option value="ERROR">ERROR</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" id="enable_belief_state" bind:checked={settings.logging.enable_belief_state} class="h-4 w-4 text-blue-600 border-gray-300 rounded" />
|
||||
<span class="ml-2 block text-sm text-gray-900">Enable Belief State Logging (Beta)</span>
|
||||
</label>
|
||||
<p class="text-xs text-gray-500 mt-1 ml-6">Logs agent reasoning and internal state changes for debugging.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<button
|
||||
on:click={handleSave}
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>
|
||||
Save Logging Config
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'connections'}
|
||||
<!-- Connections Tab -->
|
||||
<div class="text-lg font-medium mb-4">
|
||||
<h2 class="text-xl font-bold mb-4">{$t.settings?.connections || 'Database Connections'}</h2>
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.connections_description || 'Configure database connections for data mapping.'}
|
||||
</p>
|
||||
|
||||
{#if settings.connections && settings.connections.length > 0}
|
||||
<!-- Connections list would go here -->
|
||||
<p class="text-gray-500 italic">No additional connections configured. Superset database connections are used by default.</p>
|
||||
{:else}
|
||||
<div class="text-center py-8 bg-gray-50 rounded-lg border border-dashed border-gray-300">
|
||||
<p class="text-gray-500">No external connections configured.</p>
|
||||
<button class="mt-4 px-4 py-2 border border-blue-600 text-blue-600 rounded hover:bg-blue-50">
|
||||
Add Connection
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if activeTab === 'llm'}
|
||||
<!-- LLM Tab -->
|
||||
<div class="text-lg font-medium mb-4">
|
||||
<h2 class="text-xl font-bold mb-4">{$t.settings?.llm || 'LLM Providers'}</h2>
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.llm_description || 'Configure LLM providers for dataset documentation.'}
|
||||
</p>
|
||||
|
||||
<ProviderConfig providers={settings.llm_providers || []} onSave={loadSettings} />
|
||||
</div>
|
||||
{:else if activeTab === 'storage'}
|
||||
<!-- Storage Tab -->
|
||||
@@ -280,6 +504,32 @@
|
||||
<p class="text-gray-600 mb-6">
|
||||
{$t.settings?.storage_description || 'Configure file storage paths and patterns.'}
|
||||
</p>
|
||||
|
||||
<div class="bg-gray-50 p-6 rounded-lg border border-gray-200">
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<div>
|
||||
<label for="storage_path" class="block text-sm font-medium text-gray-700">Root Path</label>
|
||||
<input type="text" id="storage_path" bind:value={settings.storage.root_path} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="backup_path" class="block text-sm font-medium text-gray-700">Backup Path</label>
|
||||
<input type="text" id="backup_path" bind:value={settings.storage.backup_path} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="repo_path" class="block text-sm font-medium text-gray-700">Repository Path</label>
|
||||
<input type="text" id="repo_path" bind:value={settings.storage.repo_path} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<button
|
||||
on:click={() => handleSave()}
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>
|
||||
Save Storage Config
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
16
frontend/src/routes/storage/+page.svelte
Normal file
16
frontend/src/routes/storage/+page.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<!-- [DEF:StorageIndexPage:Page] -->
|
||||
<!--
|
||||
@TIER: TRIVIAL
|
||||
@PURPOSE: Redirect to the backups page as the default storage view.
|
||||
@LAYER: Page
|
||||
@INVARIANT: Always redirects to /storage/backups.
|
||||
-->
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
onMount(() => {
|
||||
goto('/storage/backups');
|
||||
});
|
||||
</script>
|
||||
<!-- [/DEF:StorageIndexPage:Page] -->
|
||||
35
frontend/src/routes/storage/backups/+page.svelte
Normal file
35
frontend/src/routes/storage/backups/+page.svelte
Normal file
@@ -0,0 +1,35 @@
|
||||
<!-- [DEF:StorageBackupsPage:Page] -->
|
||||
<!--
|
||||
@TIER: STANDARD
|
||||
@SEMANTICS: backup, page, tools
|
||||
@PURPOSE: Entry point for the Backup Management interface (moved from /tools/backups).
|
||||
@LAYER: Page
|
||||
@RELATION: USES -> BackupManager
|
||||
|
||||
@INVARIANT: BackupManager component is always rendered.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
/**
|
||||
* @UX_STATE: Loading -> (via BackupManager) showing spinner.
|
||||
* @UX_STATE: Idle -> Showing BackupManager interface.
|
||||
* @UX_FEEDBACK: Toast -> (via BackupManager) success/error notifications.
|
||||
*/
|
||||
// [SECTION: IMPORTS]
|
||||
import { t } from '$lib/i18n';
|
||||
import { PageHeader } from '$lib/ui';
|
||||
import BackupManager from '../../../components/backups/BackupManager.svelte';
|
||||
// [/SECTION]
|
||||
</script>
|
||||
|
||||
<!-- [SECTION: TEMPLATE] -->
|
||||
<div class="container mx-auto p-4 max-w-6xl">
|
||||
<PageHeader title={$t.nav?.backups || "Backups"} />
|
||||
|
||||
<div class="mt-6">
|
||||
<BackupManager />
|
||||
</div>
|
||||
</div>
|
||||
<!-- [/SECTION] -->
|
||||
|
||||
<!-- [/DEF:StorageBackupsPage:Page] -->
|
||||
110
frontend/src/routes/storage/repos/+page.svelte
Normal file
110
frontend/src/routes/storage/repos/+page.svelte
Normal file
@@ -0,0 +1,110 @@
|
||||
<!-- [DEF:frontend.src.routes.storage.repos.+page:Module] -->
|
||||
<!--
|
||||
@TIER: STANDARD
|
||||
@SEMANTICS: git, dashboard, management, ui
|
||||
@PURPOSE: Dashboard management page for Git integration (moved from /git).
|
||||
@LAYER: UI (Page)
|
||||
@RELATION: DEPENDS_ON -> DashboardGrid
|
||||
@RELATION: DEPENDS_ON -> api
|
||||
@INVARIANT: Dashboard grid is always shown when an environment is selected.
|
||||
-->
|
||||
|
||||
<!-- [DEF:StorageReposPage:Page] -->
|
||||
<script lang="ts">
|
||||
/**
|
||||
* @UX_STATE: Loading -> Showing spinner while fetching environments/dashboards.
|
||||
* @UX_STATE: Idle -> Showing dashboard grid with actions.
|
||||
* @UX_FEEDBACK: Toast -> Error messages on fetch failure.
|
||||
* @UX_RECOVERY: Environment Selection -> Switch environment to retry loading.
|
||||
*/
|
||||
import { onMount } from 'svelte';
|
||||
import DashboardGrid from '../../../components/DashboardGrid.svelte';
|
||||
import { addToast as toast } from '$lib/toasts.js';
|
||||
import { api } from '$lib/api.js';
|
||||
import type { DashboardMetadata } from '$lib/types/dashboard';
|
||||
import { t } from '$lib/i18n';
|
||||
import { Button, Card, PageHeader, Select } from '$lib/ui';
|
||||
|
||||
let environments: any[] = [];
|
||||
let selectedEnvId = "";
|
||||
let dashboards: DashboardMetadata[] = [];
|
||||
let loading = true;
|
||||
let fetchingDashboards = false;
|
||||
|
||||
// [DEF:fetchEnvironments:Function]
|
||||
/**
|
||||
* @PURPOSE: Fetches the list of available environments.
|
||||
* @PRE: None.
|
||||
* @POST: environments array is populated, selectedEnvId is set to first env if available.
|
||||
*/
|
||||
async function fetchEnvironments() {
|
||||
try {
|
||||
environments = await api.getEnvironmentsList();
|
||||
if (environments.length > 0) {
|
||||
selectedEnvId = environments[0].id;
|
||||
}
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
// [/DEF:fetchEnvironments:Function]
|
||||
|
||||
// [DEF:fetchDashboards:Function]
|
||||
/**
|
||||
* @PURPOSE: Fetches dashboards for a specific environment.
|
||||
* @PRE: envId is a valid environment ID.
|
||||
* @POST: dashboards array is populated with metadata for the selected environment.
|
||||
*/
|
||||
async function fetchDashboards(envId: string) {
|
||||
if (!envId) return;
|
||||
fetchingDashboards = true;
|
||||
try {
|
||||
dashboards = await api.requestApi(`/environments/${envId}/dashboards`);
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
dashboards = [];
|
||||
} finally {
|
||||
fetchingDashboards = false;
|
||||
}
|
||||
}
|
||||
// [/DEF:fetchDashboards:Function]
|
||||
|
||||
onMount(fetchEnvironments);
|
||||
|
||||
$: if (selectedEnvId) {
|
||||
fetchDashboards(selectedEnvId);
|
||||
localStorage.setItem('selected_env_id', selectedEnvId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-w-6xl mx-auto p-6">
|
||||
<PageHeader title={$t.nav?.repositories || "Git Repositories"}>
|
||||
<div slot="actions" class="flex items-center space-x-4">
|
||||
<Select
|
||||
label="Environment"
|
||||
bind:value={selectedEnvId}
|
||||
options={environments.map(e => ({ value: e.id, label: e.name }))}
|
||||
/>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
{#if loading}
|
||||
<div class="flex justify-center py-12">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
</div>
|
||||
{:else}
|
||||
<Card title="Select Dashboard to Manage">
|
||||
{#if fetchingDashboards}
|
||||
<p class="text-gray-500">Loading dashboards...</p>
|
||||
{:else if dashboards.length > 0}
|
||||
<DashboardGrid {dashboards} />
|
||||
{:else}
|
||||
<p class="text-gray-500 italic">No dashboards found in this environment.</p>
|
||||
{/if}
|
||||
</Card>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- [/DEF:StorageReposPage:Page] -->
|
||||
<!-- [/DEF:frontend.src.routes.storage.repos.+page:Module] -->
|
||||
298
logs/лог.md
298
logs/лог.md
@@ -1,298 +0,0 @@
|
||||
PS H:\dev\ss-tools> & C:/ProgramData/anaconda3/python.exe h:/dev/ss-tools/migration_script.py
|
||||
2025-12-16 11:50:28,192 - INFO - [run][Entry] Запуск скрипта миграции.
|
||||
|
||||
=== Поведение при ошибке импорта ===
|
||||
Если импорт завершится ошибкой, удалить существующий дашборд и попытаться импортировать заново? (y/n): n
|
||||
2025-12-16 11:50:33,363 - INFO - [ask_delete_on_failure][State] Delete-on-failure = False
|
||||
2025-12-16 11:50:33,368 - INFO - [select_environments][Entry] Шаг 1/5: Выбор окружений.
|
||||
2025-12-16 11:50:33,374 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-16 11:50:33,730 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-16 11:50:33,734 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-16 11:50:33,739 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-16 11:50:33,742 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-16 11:50:33,746 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-16 11:50:33,750 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-16 11:50:33,754 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-16 11:50:33,758 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-16 11:50:33,761 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-16 11:50:33,764 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-16 11:50:33,769 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-16 11:50:33,772 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-16 11:50:33,776 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-16 11:50:33,779 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-16 11:50:33,782 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-16 11:50:33,786 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-16 11:50:33,790 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-16 11:50:33,794 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-16 11:50:33,799 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-16 11:50:33,805 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-16 11:50:33,808 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-16 11:50:33,811 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-16 11:50:33,815 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-16 11:50:33,820 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-16 11:50:33,823 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-16 11:50:33,827 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-16 11:50:33,831 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-16 11:50:33,834 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-16 11:50:33,838 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-16 11:50:33,840 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-16 11:50:33,847 - INFO - [setup_clients][Exit] All clients (dev, prod, sbx, preprod, uatta, dev5) initialized successfully.
|
||||
|
||||
=== Выбор окружения ===
|
||||
Исходное окружение:
|
||||
1) dev
|
||||
2) prod
|
||||
3) sbx
|
||||
4) preprod
|
||||
5) uatta
|
||||
6) dev5
|
||||
|
||||
Введите номер (0 – отмена): 4
|
||||
2025-12-16 11:50:42,379 - INFO - [select_environments][State] from = preprod
|
||||
|
||||
=== Выбор окружения ===
|
||||
Целевое окружение:
|
||||
1) dev
|
||||
2) prod
|
||||
3) sbx
|
||||
4) uatta
|
||||
5) dev5
|
||||
|
||||
Введите номер (0 – отмена): 5
|
||||
2025-12-16 11:50:45,176 - INFO - [select_environments][State] to = dev5
|
||||
2025-12-16 11:50:45,182 - INFO - [select_environments][Exit] Шаг 1 завершён.
|
||||
2025-12-16 11:50:45,186 - INFO - [select_dashboards][Entry] Шаг 2/5: Выбор дашбордов.
|
||||
2025-12-16 11:50:45,190 - INFO - [get_dashboards][Enter] Fetching dashboards.
|
||||
2025-12-16 11:50:45,197 - INFO - [authenticate][Enter] Authenticating to https://preprodta.bi.dwh.rusal.com/api/v1
|
||||
2025-12-16 11:50:45,880 - INFO - [authenticate][Exit] Authenticated successfully.
|
||||
2025-12-16 11:50:46,025 - INFO - [get_dashboards][Exit] Found 95 dashboards.
|
||||
|
||||
=== Поиск ===
|
||||
Введите регулярное выражение для поиска дашбордов:
|
||||
fi
|
||||
|
||||
=== Выбор дашбордов ===
|
||||
Отметьте нужные дашборды (введите номера):
|
||||
1) [ALL] Все дашборды
|
||||
2) [185] FI-0060 Финансы. Налоги. Данные по налогам. Старый
|
||||
3) [184] FI-0083 Статистика по ДЗ/ПДЗ
|
||||
4) [187] FI-0081 ПДЗ Казначейство
|
||||
5) [122] FI-0080 Финансы. Оборотный Капитал ДЗ/КЗ
|
||||
6) [208] FI-0020 Просроченная дебиторская и кредиторская задолженность в динамике
|
||||
7) [126] FI-0022 Кредиторская задолженность для казначейства
|
||||
8) [196] FI-0023 Дебиторская задолженность для казначейства
|
||||
9) [113] FI-0060 Финансы. Налоги. Данные по налогам.
|
||||
10) [173] FI-0040 Оборотно-сальдовая ведомость (ОСВ) по контрагентам
|
||||
11) [174] FI-0021 Дебиторская и кредиторская задолженность по документам
|
||||
12) [172] FI-0030 Дебиторская задолженность по штрафам
|
||||
13) [170] FI-0050 Налог на прибыль (ОНА и ОНО)
|
||||
14) [159] FI-0070 Досье контрагента
|
||||
|
||||
Введите номера через запятую (пустой ввод → отказ): 2
|
||||
2025-12-16 11:50:52,235 - INFO - [select_dashboards][State] Выбрано 1 дашбордов.
|
||||
2025-12-16 11:50:52,242 - INFO - [select_dashboards][Exit] Шаг 2 завершён.
|
||||
|
||||
=== Замена БД ===
|
||||
Заменить конфигурацию БД в YAML‑файлах? (y/n): y
|
||||
2025-12-16 11:50:53,808 - INFO - [_select_databases][Entry] Selecting databases from both environments.
|
||||
2025-12-16 11:50:53,816 - INFO - [get_databases][Enter] Fetching databases.
|
||||
2025-12-16 11:50:53,918 - INFO - [get_databases][Exit] Found 12 databases.
|
||||
2025-12-16 11:50:53,923 - INFO - [get_databases][Enter] Fetching databases.
|
||||
2025-12-16 11:50:53,926 - INFO - [authenticate][Enter] Authenticating to https://dev.bi.dwh.rusal.com/api/v1
|
||||
2025-12-16 11:50:54,450 - INFO - [authenticate][Exit] Authenticated successfully.
|
||||
2025-12-16 11:50:54,551 - INFO - [get_databases][Exit] Found 4 databases.
|
||||
|
||||
=== Выбор исходной БД ===
|
||||
Выберите исходную БД:
|
||||
1) DEV datalab (ID: 9)
|
||||
2) Prod Greenplum (ID: 7)
|
||||
3) DEV Clickhouse New (OLD) (ID: 16)
|
||||
4) Preprod Clickhouse New (ID: 15)
|
||||
5) DEV Greenplum (ID: 1)
|
||||
6) Prod Clickhouse Node 1 (ID: 11)
|
||||
7) Preprod Postgre Superset Internal (ID: 5)
|
||||
8) Prod Postgre Superset Internal (ID: 28)
|
||||
9) Prod Clickhouse (ID: 10)
|
||||
10) Dev Clickhouse (correct) (ID: 14)
|
||||
11) DEV ClickHouse New (ID: 23)
|
||||
12) Sandbox Postgre Superset Internal (ID: 12)
|
||||
|
||||
Введите номер (0 – отмена): 9
|
||||
2025-12-16 11:51:11,008 - INFO - [get_database][Enter] Fetching database 10.
|
||||
2025-12-16 11:51:11,038 - INFO - [get_database][Exit] Got database 10.
|
||||
|
||||
=== Выбор целевой БД ===
|
||||
Выберите целевую БД:
|
||||
1) DEV Greenplum (ID: 2)
|
||||
2) DEV Clickhouse (ID: 3)
|
||||
3) DEV ClickHouse New (ID: 4)
|
||||
4) Dev Postgre Superset Internal (ID: 1)
|
||||
|
||||
Введите номер (0 – отмена): 2
|
||||
2025-12-16 11:51:15,559 - INFO - [get_database][Enter] Fetching database 3.
|
||||
2025-12-16 11:51:15,586 - INFO - [get_database][Exit] Got database 3.
|
||||
2025-12-16 11:51:15,589 - INFO - [_select_databases][Exit] Selected databases: Без имени -> Без имени
|
||||
old_db: {'id': 10, 'result': {'allow_ctas': False, 'allow_cvas': False, 'allow_dml': True, 'allow_file_upload': False, 'allow_run_async': False, 'backen
|
||||
d': 'clickhousedb', 'cache_timeout': None, 'configuration_method': 'sqlalchemy_form', 'database_name': 'Prod Clickhouse', 'driver': 'connect', 'engine_i
|
||||
nformation': {'disable_ssh_tunneling': False, 'supports_file_upload': False}, 'expose_in_sqllab': True, 'force_ctas_schema': None, 'id': 10, 'impersonat
|
||||
e_user': False, 'is_managed_externally': False, 'uuid': '97aced68-326a-4094-b381-27980560efa9'}}
|
||||
2025-12-16 11:51:15,591 - INFO - [confirm_db_config_replacement][State] Replacement set: {'old': {'database_name': None, 'uuid': None, 'id': '10'}, 'new
|
||||
': {'database_name': None, 'uuid': None, 'id': '3'}}
|
||||
2025-12-16 11:51:15,594 - INFO - [execute_migration][Entry] Starting migration of 1 dashboards.
|
||||
|
||||
=== Миграция... ===
|
||||
Миграция: FI-0060 Финансы. Налоги. Данные по налогам. Старый (1/1) 0%2025-12-16 11:51:15,598 - INFO - [export_dashboard][Enter] Exporting dashboard 185.
|
||||
2025-12-16 11:51:16,142 - INFO - [export_dashboard][Exit] Exported dashboard 185 to dashboard_export_20251216T085115.zip.
|
||||
2025-12-16 11:51:16,205 - INFO - [update_yamls][Enter] Starting YAML configuration update.
|
||||
2025-12-16 11:51:16,208 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\metadata.yaml
|
||||
2025-12-16 11:51:16,209 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-01_2787.yaml
|
||||
2025-12-16 11:51:16,210 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_2_4030.yaml
|
||||
2025-12-16 11:51:16,212 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_4029.yaml
|
||||
2025-12-16 11:51:16,213 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_TOTAL2_4036.yaml
|
||||
2025-12-16 11:51:16,215 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_TOTAL2_4037.yaml
|
||||
2025-12-16 11:51:16,216 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_TOTAL_4028.yaml
|
||||
2025-12-16 11:51:16,217 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_ZNODE_ROOT2_4024.yaml
|
||||
2025-12-16 11:51:16,218 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-01_ZNODE_ROOT_4033.yaml
|
||||
2025-12-16 11:51:16,220 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-02_ZFUND-BD2_4021.yaml
|
||||
2025-12-16 11:51:16,221 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-02_ZFUND_4027.yaml
|
||||
2025-12-16 11:51:16,222 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02-02_ZFUND_4034.yaml
|
||||
2025-12-16 11:51:16,224 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02_ZTAX_4022.yaml
|
||||
2025-12-16 11:51:16,226 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-02_ZTAX_4035.yaml
|
||||
2025-12-16 11:51:16,227 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-04-2_4031.yaml
|
||||
2025-12-16 11:51:16,228 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-05-01_4026.yaml
|
||||
2025-12-16 11:51:16,230 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-05-01_4032.yaml
|
||||
2025-12-16 11:51:16,231 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-06_1_4023.yaml
|
||||
2025-12-16 11:51:16,233 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060-06_2_4020.yaml
|
||||
2025-12-16 11:51:16,234 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\charts\FI-0060_4025.yaml
|
||||
2025-12-16 11:51:16,236 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\dashboards\FI-0060_185.yaml
|
||||
2025-12-16 11:51:16,238 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\databases\Prod_Clickhouse_10.yaml
|
||||
2025-12-16 11:51:16,240 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0000_-_685.yaml
|
||||
2025-12-16 11:51:16,241 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-01-2_zfund_reciever_-_861.yaml
|
||||
2025-12-16 11:51:16,242 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-01_zfund_reciever_click_689.yaml
|
||||
2025-12-16 11:51:16,244 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-02_680.yaml
|
||||
2025-12-16 11:51:16,245 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-03_ztax_862.yaml
|
||||
2025-12-16 11:51:16,246 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-04_zpbe_681.yaml
|
||||
2025-12-16 11:51:16,247 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-05_ZTAXZFUND_679.yaml
|
||||
2025-12-16 11:51:16,249 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-06_860.yaml
|
||||
2025-12-16 11:51:16,250 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-08_682.yaml
|
||||
2025-12-16 11:51:16,251 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-10_zpbe_688.yaml
|
||||
2025-12-16 11:51:16,253 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060-11_ZTAX_NAME_863.yaml
|
||||
2025-12-16 11:51:16,254 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060_683.yaml
|
||||
2025-12-16 11:51:16,255 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060_684.yaml
|
||||
2025-12-16 11:51:16,256 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060_686.yaml
|
||||
2025-12-16 11:51:16,258 - INFO - [_update_yaml_file][State] Replaced '10' with '3' for key id in C:\Users\LO54FB~1\Temp\tmpuidfegpd.dir\dashboard_export
|
||||
_20251216T085115\datasets\Prod_Clickhouse_10\FI-0060_690.yaml
|
||||
2025-12-16 11:51:16,259 - INFO - [create_dashboard_export][Enter] Packing dashboard: ['C:\\Users\\LO54FB~1\\Temp\\tmpuidfegpd.dir'] -> C:\Users\LO54FB~1
|
||||
\Temp\tmps7cuv2ti.zip
|
||||
2025-12-16 11:51:16,347 - INFO - [create_dashboard_export][Exit] Archive created: C:\Users\LO54FB~1\Temp\tmps7cuv2ti.zip
|
||||
2025-12-16 11:51:16,372 - ERROR - [import_dashboard][Failure] First import attempt failed: [API_FAILURE] API error during upload: {"errors": [{"message"
|
||||
: "Expecting value: line 1 column 1 (char 0)", "error_type": "GENERIC_BACKEND_ERROR", "level": "error", "extra": {"issue_codes": [{"code": 1011, "messag
|
||||
e": "Issue 1011 - \u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448
|
||||
\u0438\u0431\u043a\u0430."}]}}]} | Context: {'type': 'api_call'}
|
||||
Traceback (most recent call last):
|
||||
File "h:\dev\ss-tools\superset_tool\utils\network.py", line 186, in _perform_upload
|
||||
response.raise_for_status()
|
||||
File "C:\ProgramData\anaconda3\Lib\site-packages\requests\models.py", line 1021, in raise_for_status
|
||||
raise HTTPError(http_error_msg, response=self)
|
||||
requests.exceptions.HTTPError: 500 Server Error: INTERNAL SERVER ERROR for url: https://dev.bi.dwh.rusal.com/api/v1/dashboard/import/
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "h:\dev\ss-tools\superset_tool\client.py", line 141, in import_dashboard
|
||||
return self._do_import(file_path)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "h:\dev\ss-tools\superset_tool\client.py", line 197, in _do_import
|
||||
return self.network.upload_file(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "h:\dev\ss-tools\superset_tool\utils\network.py", line 172, in upload_file
|
||||
return self._perform_upload(full_url, files_payload, extra_data, _headers, timeout)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "h:\dev\ss-tools\superset_tool\utils\network.py", line 196, in _perform_upload
|
||||
raise SupersetAPIError(f"API error during upload: {e.response.text}") from e
|
||||
superset_tool.exceptions.SupersetAPIError: [API_FAILURE] API error during upload: {"errors": [{"message": "Expecting value: line 1 column 1 (char 0)", "
|
||||
error_type": "GENERIC_BACKEND_ERROR", "level": "error", "extra": {"issue_codes": [{"code": 1011, "message": "Issue 1011 - \u041f\u0440\u043e\u0438\u0437
|
||||
\u043e\u0448\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."}]}}]} | Context: {'ty
|
||||
pe': 'api_call'}
|
||||
2025-12-16 11:51:16,511 - ERROR - [execute_migration][Failure] [API_FAILURE] API error during upload: {"errors": [{"message": "Expecting value: line 1 c
|
||||
olumn 1 (char 0)", "error_type": "GENERIC_BACKEND_ERROR", "level": "error", "extra": {"issue_codes": [{"code": 1011, "message": "Issue 1011 - \u041f\u04
|
||||
40\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."}]
|
||||
}}]} | Context: {'type': 'api_call'}
|
||||
Traceback (most recent call last):
|
||||
File "h:\dev\ss-tools\superset_tool\utils\network.py", line 186, in _perform_upload
|
||||
response.raise_for_status()
|
||||
File "C:\ProgramData\anaconda3\Lib\site-packages\requests\models.py", line 1021, in raise_for_status
|
||||
raise HTTPError(http_error_msg, response=self)
|
||||
requests.exceptions.HTTPError: 500 Server Error: INTERNAL SERVER ERROR for url: https://dev.bi.dwh.rusal.com/api/v1/dashboard/import/
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "h:\dev\ss-tools\migration_script.py", line 366, in execute_migration
|
||||
self.to_c.import_dashboard(file_name=tmp_new_zip, dash_id=dash_id, dash_slug=dash_slug)
|
||||
File "h:\dev\ss-tools\superset_tool\client.py", line 141, in import_dashboard
|
||||
return self._do_import(file_path)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "h:\dev\ss-tools\superset_tool\client.py", line 197, in _do_import
|
||||
return self.network.upload_file(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "h:\dev\ss-tools\superset_tool\utils\network.py", line 172, in upload_file
|
||||
return self._perform_upload(full_url, files_payload, extra_data, _headers, timeout)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "h:\dev\ss-tools\superset_tool\utils\network.py", line 196, in _perform_upload
|
||||
raise SupersetAPIError(f"API error during upload: {e.response.text}") from e
|
||||
superset_tool.exceptions.SupersetAPIError: [API_FAILURE] API error during upload: {"errors": [{"message": "Expecting value: line 1 column 1 (char 0)", "
|
||||
error_type": "GENERIC_BACKEND_ERROR", "level": "error", "extra": {"issue_codes": [{"code": 1011, "message": "Issue 1011 - \u041f\u0440\u043e\u0438\u0437
|
||||
\u043e\u0448\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."}]}}]} | Context: {'ty
|
||||
pe': 'api_call'}
|
||||
|
||||
=== Ошибка ===
|
||||
Не удалось мигрировать дашборд FI-0060 Финансы. Налоги. Данные по налогам. Старый.
|
||||
|
||||
[API_FAILURE] API error during upload: {"errors": [{"message": "Expecting value: line 1 column 1 (char 0)", "error_type": "GENERIC_BACKEND_ERROR", "leve
|
||||
l": "error", "extra": {"issue_codes": [{"code": 1011, "message": "Issue 1011 - \u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0438
|
||||
\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."}]}}]} | Context: {'type': 'api_call'}
|
||||
|
||||
100%
|
||||
2025-12-16 11:51:16,598 - INFO - [execute_migration][Exit] Migration finished.
|
||||
|
||||
=== Информация ===
|
||||
Миграция завершена!
|
||||
|
||||
2025-12-16 11:51:16,605 - INFO - [run][Exit] Скрипт миграции завершён.
|
||||
36
reproduce_mapping_issue.py
Normal file
36
reproduce_mapping_issue.py
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
import requests
|
||||
import json
|
||||
import sys
|
||||
|
||||
# Try to find the port from app.py or common defaults
|
||||
BASE_URL = "http://127.0.0.1:8000/api"
|
||||
|
||||
def test_save_mapping():
|
||||
payload = {
|
||||
"source_env_id": "ss1",
|
||||
"target_env_id": "ss2",
|
||||
"source_db_uuid": "test-uuid-1",
|
||||
"target_db_uuid": "test-uuid-2",
|
||||
"source_db_name": "Test Source DB",
|
||||
"target_db_name": "Test Target DB"
|
||||
}
|
||||
|
||||
print(f"Sending request to {BASE_URL}/mappings with payload: {json.dumps(payload, indent=2)}")
|
||||
try:
|
||||
# Note: We might need authentication headers if has_permission is active
|
||||
# In a real tool use, we'd need to handle that, but for local testing
|
||||
# let's see if the server is even running and if we get a 401/403 or something else.
|
||||
response = requests.post(f"{BASE_URL}/mappings", json=payload)
|
||||
print(f"Status Code: {response.status_code}")
|
||||
try:
|
||||
print(f"Response Body: {json.dumps(response.json(), indent=2)}")
|
||||
except:
|
||||
print(f"Raw Response: {response.text}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
BASE_URL = sys.argv[1]
|
||||
test_save_mapping()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -353,6 +353,7 @@ export const activityStore = derived(
|
||||
<!-- @UX_FEEDBACK: Mapped % column shows progress bar + percentage text -->
|
||||
<!-- @UX_FEEDBACK: Tables column shows count of SQL tables extracted -->
|
||||
<!-- @UX_FEEDBACK: Columns column shows "X/Y" format (mapped/total) -->
|
||||
<!-- @UX_FEEDBACK: Start Mapping button is disabled until valid source is configured -->
|
||||
<!-- @UX_RECOVERY: Failed mapping shows error toast with "Retry" action -->
|
||||
|
||||
<!-- @PRE: User has permission plugin:mapper:execute for Map Columns -->
|
||||
|
||||
@@ -249,8 +249,8 @@ All implementation tasks MUST follow the Design-by-Contract specifications:
|
||||
- [x] T062 [US4] Implement pagination controls with page numbers and "Rows per page" dropdown
|
||||
- [x] T063 [US4] Create floating bulk action panel at bottom: "[✓ N selected] [Map Columns] [Generate Docs] [Validate]"
|
||||
_Contract: @UX_STATE: Selecting, @UX_FEEDBACK: Floating panel slides up_
|
||||
- [x] T064 [US4] Implement Column Mapping modal with PostgreSQL comments/XLSX source selection and preview
|
||||
_Contract: @POST: Map Columns modal shows source selection (PostgreSQL or XLSX)_
|
||||
- [x] T064 [US4] Implement Column Mapping modal with PostgreSQL comments/XLSX source selection and preview
|
||||
_Contract: @POST: Map Columns modal shows source selection (PostgreSQL or XLSX) with validation_
|
||||
- [x] T065 [US4] Implement Documentation Generation modal with LLM provider selection and options
|
||||
_Contract: @POST: Generate Docs modal shows LLM provider selection_
|
||||
- [x] T066 [US4] Create dataset detail view showing SQL tables, column counts, mapping percentages, and linked dashboards
|
||||
|
||||
@@ -131,6 +131,47 @@
|
||||
- 📝 Clears authentication state and storage.
|
||||
- ƒ **setLoading** (`Function`)
|
||||
- 📝 Updates the loading state.
|
||||
- 📦 **debounce** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/utils/debounce.js
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **debounce** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🗄️ **taskDrawer** (`Store`) `[CRITICAL]`
|
||||
- 📝 Manage Task Drawer visibility and resource-to-task mapping
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: resourceTaskMap always reflects current task associations
|
||||
- 📦 **taskDrawer** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/stores/taskDrawer.js
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **openDrawerForTask** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **closeDrawer** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **updateResourceTask** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **getTaskForResource** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🗄️ **sidebar** (`Store`)
|
||||
- 📝 Manage sidebar visibility and navigation state
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: isExpanded state is always synced with localStorage
|
||||
- 📦 **sidebar** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/stores/sidebar.js
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **toggleSidebar** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **setActiveItem** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **setMobileOpen** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **closeMobile** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **toggleMobileSidebar** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🗄️ **activity** (`Store`)
|
||||
- 📝 Track active task count for navbar indicator
|
||||
- 🏗️ Layer: UI
|
||||
- 🔗 DEPENDS_ON -> `WebSocket connection, taskDrawer store`
|
||||
- 🧩 **Select** (`Component`) `[TRIVIAL]`
|
||||
- 📝 Standardized dropdown selection component.
|
||||
- 🏗️ Layer: Atom
|
||||
@@ -172,12 +213,95 @@
|
||||
- 📝 Holds the current active locale string.
|
||||
- 🗄️ **t** (`Store`)
|
||||
- 📝 Derived store providing the translation dictionary.
|
||||
- ƒ **selectPlugin** (`Function`)
|
||||
- 📝 Handles plugin selection and navigation.
|
||||
- ƒ **handleFormSubmit** (`Function`)
|
||||
- 📝 Handles task creation from dynamic form submission.
|
||||
- ƒ **_** (`Function`)
|
||||
- 📝 Get translation by key path.
|
||||
- 🧩 **Sidebar** (`Component`) `[CRITICAL]`
|
||||
- 📝 Persistent left sidebar with resource categories navigation
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always shows active category and item
|
||||
- ⬅️ READS_FROM `app`
|
||||
- ⬅️ READS_FROM `lib`
|
||||
- ⬅️ READS_FROM `t`
|
||||
- 📦 **Sidebar** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/components/layout/Sidebar.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **handleItemClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleCategoryToggle** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSubItemClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleToggleClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleOverlayClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🧩 **TopNavbar** (`Component`) `[CRITICAL]`
|
||||
- 📝 Unified top navigation bar with Logo, Search, Activity, and User menu
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always visible on non-login pages
|
||||
- ⚡ Events: activityClick
|
||||
- ⬅️ READS_FROM `app`
|
||||
- ⬅️ READS_FROM `lib`
|
||||
- ⬅️ READS_FROM `sidebarStore`
|
||||
- 📦 **TopNavbar** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/components/layout/TopNavbar.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **toggleUserMenu** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **closeUserMenu** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleLogout** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleActivityClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSearchFocus** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSearchBlur** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleDocumentClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleHamburgerClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🧩 **Breadcrumbs** (`Component`)
|
||||
- 📝 Display page hierarchy navigation
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always shows current page path
|
||||
- 📥 Props: maxVisible: any
|
||||
- ⬅️ READS_FROM `app`
|
||||
- ⬅️ READS_FROM `lib`
|
||||
- ⬅️ READS_FROM `page`
|
||||
- 📦 **Breadcrumbs** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/components/layout/Breadcrumbs.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **getBreadcrumbs** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **formatBreadcrumbLabel** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🧩 **TaskDrawer** (`Component`) `[CRITICAL]`
|
||||
- 📝 Global task drawer for monitoring background operations
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Drawer shows logs for active task or remains closed
|
||||
- ⬅️ READS_FROM `lib`
|
||||
- ⬅️ READS_FROM `taskDrawerStore`
|
||||
- ➡️ WRITES_TO `taskDrawerStore`
|
||||
- 📦 **TaskDrawer** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/lib/components/layout/TaskDrawer.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **handleClose** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleOverlayClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **connectWebSocket** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **disconnectWebSocket** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **HomePage** (`Page`) `[CRITICAL]`
|
||||
- 📝 Redirect to Dashboard Hub as per UX requirements
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always redirects to /dashboards
|
||||
- ƒ **load** (`Function`)
|
||||
- 📝 Loads initial plugin data for the dashboard.
|
||||
- 📦 **layout** (`Module`)
|
||||
- 🧩 **TaskManagementPage** (`Component`)
|
||||
- 📝 Page for managing and monitoring tasks.
|
||||
- 🏗️ Layer: Page
|
||||
@@ -192,6 +316,62 @@
|
||||
- 📝 Updates the selected task ID when a task is clicked.
|
||||
- ƒ **handleRunBackup** (`Function`)
|
||||
- 📝 Triggers a manual backup task for the selected environment.
|
||||
- 📦 **DatasetHub** (`Page`) `[CRITICAL]`
|
||||
- 📝 Dataset Hub - Dedicated hub for datasets with mapping progress
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always shows environment selector and dataset grid
|
||||
- 📦 **+page** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/routes/datasets/+page.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **loadEnvironments** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **loadDatasets** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleEnvChange** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSearch** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handlePageChange** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handlePageSizeChange** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **updateSelectionState** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleCheckboxChange** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSelectAll** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSelectVisible** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleAction** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleBulkMapColumns** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleBulkGenerateDocs** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleTaskStatusClick** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **getTaskStatusIcon** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **getMappingProgressClass** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **DatasetDetail** (`Page`) `[CRITICAL]`
|
||||
- 📝 Dataset Detail View - Shows detailed dataset information with columns, SQL, and linked dashboards
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always shows dataset details when loaded
|
||||
- 📦 **+page** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/routes/datasets/[id]/+page.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **loadDatasetDetail** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **navigateToDashboard** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **goBack** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **getColumnTypeClass** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **getMappingProgress** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 🧩 **LoginPage** (`Component`)
|
||||
- 📝 Provides the user interface for local and ADFS authentication.
|
||||
- 🏗️ Layer: UI
|
||||
@@ -202,6 +382,15 @@
|
||||
- 📝 Submits the local login form to the backend.
|
||||
- ƒ **handleADFSLogin** (`Function`)
|
||||
- 📝 Redirects the user to the ADFS login endpoint.
|
||||
- 📦 **StorageIndexPage** (`Page`) `[TRIVIAL]`
|
||||
- 📝 Redirect to the backups page as the default storage view.
|
||||
- 🏗️ Layer: Page
|
||||
- 🔒 Invariant: Always redirects to /storage/backups.
|
||||
- 📦 **StorageReposPage** (`Page`)
|
||||
- ƒ **fetchEnvironments** (`Function`)
|
||||
- 📝 Fetches the list of available environments.
|
||||
- ƒ **fetchDashboards** (`Function`)
|
||||
- 📝 Fetches dashboards for a specific environment.
|
||||
- 🧩 **AdminRolesPage** (`Component`)
|
||||
- 📝 UI for managing system roles and their permissions.
|
||||
- 🏗️ Layer: Domain
|
||||
@@ -317,20 +506,31 @@
|
||||
- 📝 Page for system diagnostics and debugging.
|
||||
- 🏗️ Layer: UI
|
||||
- ⬅️ READS_FROM `lib`
|
||||
- ƒ **handleSaveGlobal** (`Function`)
|
||||
- 📝 Saves global application settings.
|
||||
- ƒ **handleSaveStorage** (`Function`)
|
||||
- 📝 Saves storage-specific settings.
|
||||
- ƒ **handleAddOrUpdateEnv** (`Function`)
|
||||
- 📝 Adds a new environment or updates an existing one.
|
||||
- ƒ **handleDeleteEnv** (`Function`)
|
||||
- 📝 Deletes a Superset environment.
|
||||
- ƒ **handleTestEnv** (`Function`)
|
||||
- 📝 Tests the connection to a Superset environment.
|
||||
- ƒ **editEnv** (`Function`)
|
||||
- 📝 Populates the environment form for editing.
|
||||
- ƒ **resetEnvForm** (`Function`)
|
||||
- 📝 Resets the environment creation/edit form to default state.
|
||||
- 📦 **SettingsPage** (`Page`) `[CRITICAL]`
|
||||
- 📝 Consolidated Settings Page - All settings in one place with tabbed navigation
|
||||
- 🏗️ Layer: UI
|
||||
- 🔒 Invariant: Always shows tabbed interface with all settings categories
|
||||
- 📦 **+page** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for frontend/src/routes/settings/+page.svelte
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **loadSettings** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleTabChange** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **getTabClass** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleSave** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleTestEnv** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **editEnv** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **resetEnvForm** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleAddOrUpdateEnv** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **handleDeleteEnv** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **load** (`Function`)
|
||||
- 📝 Loads application settings and environment list.
|
||||
- 🧩 **ConnectionsSettingsPage** (`Component`)
|
||||
@@ -855,37 +1055,49 @@
|
||||
- 📝 Handles application shutdown tasks, such as stopping the scheduler.
|
||||
- ƒ **log_requests** (`Function`)
|
||||
- 📝 Middleware to log incoming HTTP requests and their response status.
|
||||
- 📦 **api.include_routers** (`Action`)
|
||||
- 📝 Registers all API routers with the FastAPI application.
|
||||
- 🏗️ Layer: API
|
||||
- ƒ **websocket_endpoint** (`Function`) `[CRITICAL]`
|
||||
- 📝 Provides a WebSocket endpoint for real-time log streaming of a task with server-side filtering.
|
||||
- 📦 **StaticFiles** (`Mount`)
|
||||
- 📝 Mounts the frontend build directory to serve static assets.
|
||||
- ƒ **serve_spa** (`Function`)
|
||||
- 📝 Serves frontend static files or index.html for SPA routing.
|
||||
- ƒ **read_root** (`Function`)
|
||||
- 📝 A simple root endpoint to confirm that the API is running when frontend is missing.
|
||||
- ƒ **network_error_handler** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **matches_filters** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **serve_spa** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **Dependencies** (`Module`)
|
||||
- 📝 Manages the creation and provision of shared application dependencies, such as the PluginLoader and TaskManager, to avoid circular imports.
|
||||
- 📝 Manages creation and provision of shared application dependencies, such as PluginLoader and TaskManager, to avoid circular imports.
|
||||
- 🏗️ Layer: Core
|
||||
- ƒ **get_config_manager** (`Function`)
|
||||
- 📝 Dependency injector for the ConfigManager.
|
||||
- 📝 Dependency injector for ConfigManager.
|
||||
- ƒ **get_plugin_loader** (`Function`)
|
||||
- 📝 Dependency injector for the PluginLoader.
|
||||
- 📝 Dependency injector for PluginLoader.
|
||||
- ƒ **get_task_manager** (`Function`)
|
||||
- 📝 Dependency injector for the TaskManager.
|
||||
- 📝 Dependency injector for TaskManager.
|
||||
- ƒ **get_scheduler_service** (`Function`)
|
||||
- 📝 Dependency injector for the SchedulerService.
|
||||
- 📝 Dependency injector for SchedulerService.
|
||||
- ƒ **get_resource_service** (`Function`)
|
||||
- 📝 Dependency injector for ResourceService.
|
||||
- ƒ **get_mapping_service** (`Function`)
|
||||
- 📝 Dependency injector for MappingService.
|
||||
- 📦 **oauth2_scheme** (`Variable`)
|
||||
- 📝 OAuth2 password bearer scheme for token extraction.
|
||||
- ƒ **get_current_user** (`Function`)
|
||||
- 📝 Dependency for retrieving the currently authenticated user from a JWT.
|
||||
- 📝 Dependency for retrieving currently authenticated user from a JWT.
|
||||
- ƒ **has_permission** (`Function`)
|
||||
- 📝 Dependency for checking if the current user has a specific permission.
|
||||
- ƒ **permission_checker** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **test_dataset_dashboard_relations** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for backend/src/scripts/test_dataset_dashboard_relations.py
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **test_dashboard_dataset_relations** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **backend.src.scripts.seed_permissions** (`Module`)
|
||||
- 📝 Populates the auth database with initial system permissions.
|
||||
- 🏗️ Layer: Scripts
|
||||
@@ -959,6 +1171,12 @@
|
||||
- 📝 Удаляет дашборд по его ID или slug.
|
||||
- ƒ **get_datasets** (`Function`)
|
||||
- 📝 Получает полный список датасетов, автоматически обрабатывая пагинацию.
|
||||
- ƒ **get_datasets_summary** (`Function`)
|
||||
- 📝 Fetches dataset metadata optimized for the Dataset Hub grid.
|
||||
- ƒ **get_dataset_detail** (`Function`)
|
||||
- 📝 Fetches detailed dataset information including columns and linked dashboards
|
||||
- 🔗 CALLS -> `self.get_dataset`
|
||||
- 🔗 CALLS -> `self.network.request (for related_objects)`
|
||||
- ƒ **get_dataset** (`Function`)
|
||||
- 📝 Получает информацию о конкретном датасете по его ID.
|
||||
- ƒ **update_dataset** (`Function`)
|
||||
@@ -1326,6 +1544,8 @@
|
||||
- 📝 Получает общее количество элементов для пагинации.
|
||||
- ƒ **fetch_paginated_data** (`Function`)
|
||||
- 📝 Автоматически собирает данные со всех страниц пагинированного эндпоинта.
|
||||
- ƒ **init_poolmanager** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **backend.src.core.utils.matching** (`Module`)
|
||||
- 📝 Provides utility functions for fuzzy matching database names.
|
||||
- 🏗️ Layer: Core
|
||||
@@ -1551,6 +1771,40 @@
|
||||
- 📝 Test connection to an LLM provider.
|
||||
- ƒ **test_provider_config** (`Function`)
|
||||
- 📝 Test connection with a provided configuration (not yet saved).
|
||||
- 📦 **backend.src.api.routes.datasets** (`Module`)
|
||||
- 📝 API endpoints for the Dataset Hub - listing datasets with mapping progress
|
||||
- 🏗️ Layer: API
|
||||
- 🔒 Invariant: All dataset responses include last_task metadata
|
||||
- 🔗 DEPENDS_ON -> `backend.src.dependencies`
|
||||
- 🔗 DEPENDS_ON -> `backend.src.services.resource_service`
|
||||
- 🔗 DEPENDS_ON -> `backend.src.core.superset_client`
|
||||
- 📦 **MappedFields** (`DataClass`)
|
||||
- 📦 **LastTask** (`DataClass`)
|
||||
- 📦 **DatasetItem** (`DataClass`)
|
||||
- 📦 **LinkedDashboard** (`DataClass`)
|
||||
- 📦 **DatasetColumn** (`DataClass`)
|
||||
- 📦 **DatasetDetailResponse** (`DataClass`)
|
||||
- 📦 **DatasetsResponse** (`DataClass`)
|
||||
- 📦 **TaskResponse** (`DataClass`)
|
||||
- ƒ **get_dataset_ids** (`Function`)
|
||||
- 📝 Fetch list of all dataset IDs from a specific environment (without pagination)
|
||||
- 🔗 CALLS -> `ResourceService.get_datasets_with_status`
|
||||
- ƒ **get_datasets** (`Function`)
|
||||
- 📝 Fetch list of datasets from a specific environment with mapping progress
|
||||
- 🔗 CALLS -> `ResourceService.get_datasets_with_status`
|
||||
- 📦 **MapColumnsRequest** (`DataClass`)
|
||||
- ƒ **map_columns** (`Function`)
|
||||
- 📝 Trigger bulk column mapping for datasets
|
||||
- 🔗 DISPATCHES -> `MapperPlugin`
|
||||
- 🔗 CALLS -> `task_manager.create_task`
|
||||
- 📦 **GenerateDocsRequest** (`DataClass`)
|
||||
- ƒ **generate_docs** (`Function`)
|
||||
- 📝 Trigger bulk documentation generation for datasets
|
||||
- 🔗 DISPATCHES -> `LLMAnalysisPlugin`
|
||||
- 🔗 CALLS -> `task_manager.create_task`
|
||||
- ƒ **get_dataset_detail** (`Function`)
|
||||
- 📝 Get detailed dataset information including columns and linked dashboards
|
||||
- 🔗 CALLS -> `SupersetClient.get_dataset_detail`
|
||||
- 📦 **backend.src.api.routes.git** (`Module`)
|
||||
- 📝 Provides FastAPI endpoints for Git integration operations.
|
||||
- 🏗️ Layer: API
|
||||
@@ -1615,10 +1869,13 @@
|
||||
- 📦 **DatabaseResponse** (`DataClass`)
|
||||
- ƒ **get_environments** (`Function`)
|
||||
- 📝 List all configured environments.
|
||||
- 🏗️ Layer: API
|
||||
- ƒ **update_environment_schedule** (`Function`)
|
||||
- 📝 Update backup schedule for an environment.
|
||||
- 🏗️ Layer: API
|
||||
- ƒ **get_environment_databases** (`Function`)
|
||||
- 📝 Fetch the list of databases from a specific environment.
|
||||
- 🏗️ Layer: API
|
||||
- 📦 **backend.src.api.routes.migration** (`Module`)
|
||||
- 📝 API endpoints for migration operations.
|
||||
- 🏗️ Layer: API
|
||||
@@ -1679,6 +1936,11 @@
|
||||
- 📝 Retrieves current logging configuration.
|
||||
- ƒ **update_logging_config** (`Function`)
|
||||
- 📝 Updates logging configuration.
|
||||
- ℂ **ConsolidatedSettingsResponse** (`Class`)
|
||||
- ƒ **get_consolidated_settings** (`Function`)
|
||||
- 📝 Retrieves all settings categories in a single call
|
||||
- ƒ **update_consolidated_settings** (`Function`)
|
||||
- 📝 Bulk update application settings from the consolidated view.
|
||||
- 📦 **backend.src.api.routes.admin** (`Module`)
|
||||
- 📝 Admin API endpoints for user and role management.
|
||||
- 🏗️ Layer: API
|
||||
@@ -1781,6 +2043,36 @@
|
||||
- 📝 Resume a task that is awaiting input (e.g., passwords).
|
||||
- ƒ **clear_tasks** (`Function`)
|
||||
- 📝 Clear tasks matching the status filter.
|
||||
- 📦 **backend.src.api.routes.dashboards** (`Module`)
|
||||
- 📝 API endpoints for the Dashboard Hub - listing dashboards with Git and task status
|
||||
- 🏗️ Layer: API
|
||||
- 🔒 Invariant: All dashboard responses include git_status and last_task metadata
|
||||
- 🔗 DEPENDS_ON -> `backend.src.dependencies`
|
||||
- 🔗 DEPENDS_ON -> `backend.src.services.resource_service`
|
||||
- 🔗 DEPENDS_ON -> `backend.src.core.superset_client`
|
||||
- 📦 **GitStatus** (`DataClass`)
|
||||
- 📦 **LastTask** (`DataClass`)
|
||||
- 📦 **DashboardItem** (`DataClass`)
|
||||
- 📦 **DashboardsResponse** (`DataClass`)
|
||||
- ƒ **get_dashboards** (`Function`)
|
||||
- 📝 Fetch list of dashboards from a specific environment with Git status and last task status
|
||||
- 🔗 CALLS -> `ResourceService.get_dashboards_with_status`
|
||||
- 📦 **MigrateRequest** (`DataClass`)
|
||||
- 📦 **TaskResponse** (`DataClass`)
|
||||
- ƒ **migrate_dashboards** (`Function`)
|
||||
- 📝 Trigger bulk migration of dashboards from source to target environment
|
||||
- 🔗 DISPATCHES -> `MigrationPlugin`
|
||||
- 🔗 CALLS -> `task_manager.create_task`
|
||||
- 📦 **BackupRequest** (`DataClass`)
|
||||
- ƒ **backup_dashboards** (`Function`)
|
||||
- 📝 Trigger bulk backup of dashboards with optional cron schedule
|
||||
- 🔗 DISPATCHES -> `BackupPlugin`
|
||||
- 🔗 CALLS -> `task_manager.create_task`
|
||||
- 📦 **DatabaseMapping** (`DataClass`)
|
||||
- 📦 **DatabaseMappingsResponse** (`DataClass`)
|
||||
- ƒ **get_database_mappings** (`Function`)
|
||||
- 📝 Get database mapping suggestions between source and target environments
|
||||
- 🔗 CALLS -> `MappingService.get_suggestions`
|
||||
- 📦 **backend.src.models.llm** (`Module`)
|
||||
- 📝 SQLAlchemy models for LLM provider configuration and validation results.
|
||||
- 🏗️ Layer: Domain
|
||||
@@ -1865,6 +2157,37 @@
|
||||
- ℂ **ADGroupMapping** (`Class`)
|
||||
- 📝 Maps an Active Directory group to a local System Role.
|
||||
- 🔗 DEPENDS_ON -> `Role`
|
||||
- 📦 **backend.src.services.resource_service** (`Module`)
|
||||
- 📝 Shared service for fetching resource data with Git status and task status
|
||||
- 🏗️ Layer: Service
|
||||
- 🔒 Invariant: All resources include metadata about their current state
|
||||
- 🔗 DEPENDS_ON -> `backend.src.core.superset_client`
|
||||
- 🔗 DEPENDS_ON -> `backend.src.core.task_manager`
|
||||
- 🔗 DEPENDS_ON -> `backend.src.services.git_service`
|
||||
- ℂ **ResourceService** (`Class`)
|
||||
- 📝 Provides centralized access to resource data with enhanced metadata
|
||||
- ƒ **__init__** (`Function`)
|
||||
- 📝 Initialize the resource service with dependencies
|
||||
- ƒ **get_dashboards_with_status** (`Function`)
|
||||
- 📝 Fetch dashboards from environment with Git status and last task status
|
||||
- 🔗 CALLS -> `SupersetClient.get_dashboards_summary`
|
||||
- 🔗 CALLS -> `self._get_git_status_for_dashboard`
|
||||
- 🔗 CALLS -> `self._get_last_task_for_resource`
|
||||
- ƒ **get_datasets_with_status** (`Function`)
|
||||
- 📝 Fetch datasets from environment with mapping progress and last task status
|
||||
- 🔗 CALLS -> `SupersetClient.get_datasets_summary`
|
||||
- 🔗 CALLS -> `self._get_last_task_for_resource`
|
||||
- ƒ **get_activity_summary** (`Function`)
|
||||
- 📝 Get summary of active and recent tasks for the activity indicator
|
||||
- ƒ **_get_git_status_for_dashboard** (`Function`)
|
||||
- 📝 Get Git sync status for a dashboard
|
||||
- 🔗 CALLS -> `GitService.get_repo`
|
||||
- ƒ **_get_last_task_for_resource** (`Function`)
|
||||
- 📝 Get the most recent task for a specific resource
|
||||
- ƒ **_extract_resource_name_from_task** (`Function`)
|
||||
- 📝 Extract resource name from task params
|
||||
- ƒ **_extract_resource_type_from_task** (`Function`)
|
||||
- 📝 Extract resource type from task params
|
||||
- 📦 **backend.src.services.llm_provider** (`Module`)
|
||||
- 📝 Service for managing LLM provider configurations with encrypted API keys.
|
||||
- 🏗️ Layer: Domain
|
||||
@@ -1903,6 +2226,9 @@
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **__init__** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **backend.src.services** (`Module`)
|
||||
- 📝 Package initialization for services module
|
||||
- 🏗️ Layer: Core
|
||||
- 📦 **backend.src.services.auth_service** (`Module`)
|
||||
- 📝 Orchestrates authentication business logic.
|
||||
- 🏗️ Layer: Service
|
||||
@@ -2247,6 +2573,34 @@
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_environment_model** (`Function`)
|
||||
- 📝 Tests that Environment model correctly stores values.
|
||||
- 📦 **backend.tests.test_dashboards_api** (`Module`)
|
||||
- 📝 Contract-driven tests for Dashboard Hub API
|
||||
- 🏗️ Layer: Domain (Tests)
|
||||
- ƒ **test_get_dashboards_success** (`Function`)
|
||||
- ƒ **test_get_dashboards_env_not_found** (`Function`)
|
||||
- 📦 **test_dashboards_api** (`Test`)
|
||||
- 📝 Verify GET /api/dashboards contract compliance
|
||||
- 📦 **test_datasets_api** (`Test`)
|
||||
- 📝 Verify GET /api/datasets contract compliance
|
||||
- 📦 **test_resource_hubs** (`Module`) `[TRIVIAL]`
|
||||
- 📝 Auto-generated module for backend/tests/test_resource_hubs.py
|
||||
- 🏗️ Layer: Unknown
|
||||
- ƒ **mock_deps** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_dashboards_success** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_dashboards_not_found** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_dashboards_search** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_datasets_success** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_datasets_not_found** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_datasets_search** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_get_datasets_service_failure** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- 📦 **test_task_logger** (`Module`)
|
||||
- 📝 Unit tests for TaskLogger and TaskContext.
|
||||
- 🏗️ Layer: Test
|
||||
@@ -2298,6 +2652,11 @@
|
||||
- 📝 Test sub-context logger uses new source.
|
||||
- ƒ **test_multiple_sub_contexts** (`Function`)
|
||||
- 📝 Test creating multiple sub-contexts.
|
||||
- 📦 **backend.tests.test_resource_service** (`Module`)
|
||||
- 📝 Contract-driven tests for ResourceService
|
||||
- ƒ **test_get_dashboards_with_status** (`Function`)
|
||||
- ƒ **test_get_dashboards_with_status** (`Function`) `[TRIVIAL]`
|
||||
- 📝 Auto-detected function (orphan)
|
||||
- ƒ **test_belief_scope_logs_entry_action_exit_at_debug** (`Function`)
|
||||
- 📝 Test that belief_scope generates [ID][Entry], [ID][Action], and [ID][Exit] logs at DEBUG level.
|
||||
- ƒ **test_belief_scope_error_handling** (`Function`)
|
||||
|
||||
Reference in New Issue
Block a user