feat: attention-optimized semantic protocol v2.7

Core changes:
- Add @defgroup/@ingroup to 1791 C2+ contracts (555 files) for HCA 128× pre-training DSA grouping
- Add §0.1 Pre-Training Frequency matrix to semantics-core
- Add §VIII Attention Architecture rules (ATTN_1-4) with MLA/CSA/HCA/DSA mechanics
- Add @defgroup/@ingroup to canonical syntax (§II) and all contract examples

Agent prompts (5 files):
- Add ZERO-STATE RATIONALE with MLA/CSA/HCA/DSA compression mechanics
- Add pre-training note: @RATIONALE/@REJECTED are in-context learned tags
- svelte-coder: add missing #region contract, fix Svelte rule violations
- python-coder/fullstack-coder: honor function contracts from speckit plan
- qa-tester: add attention compliance audit (P3 ATTN_1-4 checks)

Skills (6 files):
- Translate all axiom_config descriptions to English
- Fix doc_dirs to index .opencode/ and .specify/
- Deduplicate 5× complexity_rules → single global_tags catalog
- Reduce semantics-svelte 591→485 lines (remove duplicate code blocks)
- Fix semantics-testing: 'Short IDs' → 'Short hierarchical IDs'
- Fix all examples: flat IDs → hierarchical Domain.Name format
- Fix Svelte examples: replace raw Tailwind + <button> with semantic tokens + /ui

Speckit workflow (commands + templates):
- speckit.plan: add Function-Level Contracts for C3+ with @PRE/@POST/@TEST_EDGE
- speckit.plan: add Attention Compliance Gate (ATTN_1-4 before contract generation)
- speckit.tasks: add function contract inlining format (constraints in task description)
- speckit.specify: load semantics-core for spec density rules
- spec-template: add #region contract, @SEMANTICS grouping, hierarchical IDs
- ux-reference-template: add #region wrapper
- plan-template: add attention gate, @defgroup/@ingroup guidance
- tasks-template: add attention audit + rebuild + orphan check tasks
- constitution.md: translate to English, add Principle VIII (attention-optimized contracts)

Reference modules rewritten (hierarchical IDs + full contracts):
- Auth.Jwt: 6 child contracts with @RATIONALE/@REJECTED/@TEST_EDGE
- Api.Auth: 5 endpoints with @TEST_EDGE + molecular CoT markers
- Migration.Model: @defgroup Migration with 18 @ACTION + 6 @INVARIANT

Scripts:
- add_defgroup_ingroup.py: zero-risk additive @ingroup migration (1791 insertions)
- migrate_hierarchical.py: flat→hierarchical ID dry-run analysis (792 contracts)
- merge_prompts.py: merge all prompts/skills/commands into one review file

Config:
- axiom_config.yaml: 749→395 lines (-47%), English, doc_dirs include prompts
- Fix test_datasets.py import collision (rename → test_datasets_routes.py)
- Fix test_preview.py: SupersetClient→get_superset_client, AsyncMock, logger f-string
This commit is contained in:
2026-06-08 16:30:59 +03:00
parent 73e809e189
commit 8e8a3c3235
562 changed files with 2523 additions and 244 deletions

View File

@@ -1,3 +1,4 @@
# #region SrcRoot [TYPE Module] [SEMANTICS root, package]
# @defgroup Module Module group.
# @BRIEF Canonical backend package root for application, scripts, and tests.
# #endregion SrcRoot

View File

@@ -1,3 +1,4 @@
# #region src.api [TYPE Package] [SEMANTICS api, package, init]
# @ingroup Module
# @BRIEF Backend API package root.
# #endregion src.api

View File

@@ -1,14 +1,14 @@
# #region AuthApi [C:5] [TYPE Module] [SEMANTICS fastapi, auth, api]
# @BRIEF Authentication API endpoints.
# #region Api.Auth [C:5] [TYPE Module] [SEMANTICS api,auth,fastapi]
# @defgroup Auth Authentication API endpoints — login, logout, token, ADFS.
# @LAYER API
# @PRE Python environment and dependencies installed; database available.
# @POST FastAPI app instance with auth routes registered.
# @PRE Database available; JWT secret configured.
# @POST FastAPI routes registered for all auth operations.
# @SIDE_EFFECT Registers API routes; configures OAuth and CORS middleware.
# @DATA_CONTRACT Input -> OAuth2PasswordRequestForm -> Token, User
# @INVARIANT All auth endpoints must return consistent error codes.
# @RELATION DEPENDS_ON -> [is_adfs_configured]
# @REVIEWED 2026-06-03
# @TESTED true
# @DATA_CONTRACT OAuth2PasswordRequestForm -> Token | User
# @INVARIANT All auth endpoints return consistent error codes (401/403/422).
# @RELATION DEPENDS_ON -> [Auth.Jwt]
# @RELATION DEPENDS_ON -> [Auth.Service]
# @RELATION DEPENDS_ON -> [Auth.OAuth]
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
@@ -32,12 +32,17 @@ router = APIRouter(prefix="/api/auth", tags=["auth"])
# #endregion router
# #region login_for_access_token [C:4] [TYPE Function]
# @BRIEF Authenticates a user and returns a JWT access token.
# #region Api.Auth.Login [C:4] [TYPE Function] [SEMANTICS api,auth,login,token]
# @ingroup Auth
# @BRIEF Authenticate user by credentials and return JWT access token.
# @PRE form_data contains username and password.
# @POST Returns a Token object on success.
# @SIDE_EFFECT DB read/write for auth session; writes security event log.
# @RELATION CALLS -> [AuthService]
# @POST Returns Token(access_token, token_type) on success; 401 on failure.
# @SIDE_EFFECT DB read for user verification; writes security event LOGIN.
# @SIDE_EFFECT Molecular CoT: REASON on entry, REFLECT on success, EXPLORE on failure.
# @RELATION CALLS -> [Auth.Service]
# @TEST_EDGE: invalid_credentials -> 401
# @TEST_EDGE: locked_account -> 423
# @TEST_EDGE: missing_fields -> 422
@router.post("/login", response_model=Token)
async def login_for_access_token(
@@ -74,14 +79,15 @@ async def login_for_access_token(
return auth_service.create_session(user)
# #endregion login_for_access_token
# #endregion Api.Auth.Login
# #region read_users_me [C:4] [TYPE Function]
# @BRIEF Retrieves the profile of the currently authenticated user.
# @PRE Valid JWT token provided.
# @POST Returns the current user's data.
# @RELATION DEPENDS_ON -> [get_current_user]
# @SIDE_EFFECT Reads current user from DB via auth middleware; writes security event log.
# #region Api.Auth.Me [C:3] [TYPE Function] [SEMANTICS api,auth,profile]
# @ingroup Auth
# @BRIEF Retrieve the profile of the currently authenticated user.
# @PRE Valid JWT token in Authorization header.
# @POST Returns UserSchema with id, username, email, roles.
# @SIDE_EFFECT Reads current user from DB via auth middleware.
# @RELATION DEPENDS_ON -> [Auth.Dependency.GetCurrentUser]
@router.get("/me", response_model=UserSchema)
async def read_users_me(current_user: UserSchema = Depends(get_current_user)):
@@ -89,16 +95,19 @@ async def read_users_me(current_user: UserSchema = Depends(get_current_user)):
return current_user
# #endregion read_users_me
# #endregion Api.Auth.Me
# #region logout [C:4] [TYPE Function]
# @BRIEF Logs out the current user — blacklists the JWT token server-side.
# @PRE Valid JWT token provided in Authorization header.
# @POST Token is added to blacklist; subsequent requests with same token are rejected.
# @SIDE_EFFECT Writes security event LOGOUT event; writes to token_blacklist table.
# @RELATION DEPENDS_ON -> [get_current_user]
# @RELATION CALLS -> [blacklist_token]
# #region Api.Auth.Logout [C:4] [TYPE Function] [SEMANTICS api,auth,logout,revoke]
# @ingroup Auth
# @BRIEF Log out current user — blacklists the JWT token server-side.
# @PRE Valid JWT token in Authorization header.
# @POST Token added to blacklist; subsequent requests with same token rejected.
# @SIDE_EFFECT Writes security event LOGOUT; writes to token_blacklist table.
# @SIDE_EFFECT Molecular CoT: REASON/REFLECT/EXPLORE markers.
# @RELATION DEPENDS_ON -> [Auth.Dependency.GetCurrentUser]
# @RELATION CALLS -> [Auth.Jwt.BlacklistToken]
# @TEST_EDGE: already_expired_token -> 200 (idempotent)
@router.post("/logout")
async def logout(
@@ -122,14 +131,15 @@ async def logout(
return {"message": "Successfully logged out"}
# #endregion logout
# #endregion Api.Auth.Logout
# #region login_adfs [C:4] [TYPE Function]
# @BRIEF Initiates the ADFS OIDC login flow.
# @POST Redirects the user to ADFS.
# @RELATION CALLS -> [is_adfs_configured]
# @SIDE_EFFECT Redirects user to ADFS external OIDC provider.
# #region Api.Auth.LoginAdfs [C:3] [TYPE Function] [SEMANTICS api,auth,adfs,oidc]
# @ingroup Auth
# @BRIEF Initiate ADFS OIDC login flow — redirects user to identity provider.
# @POST Redirects user to ADFS authorization endpoint.
# @SIDE_EFFECT Redirects browser to external OIDC provider.
# @RELATION CALLS -> [Auth.OAuth]
@router.get("/login/adfs")
async def login_adfs(request: starlette.requests.Request):
@@ -143,14 +153,18 @@ async def login_adfs(request: starlette.requests.Request):
return await oauth.adfs.authorize_redirect(request, str(redirect_uri))
# #endregion login_adfs
# #endregion Api.Auth.LoginAdfs
# #region auth_callback_adfs [C:4] [TYPE Function]
# @BRIEF Handles the callback from ADFS after successful authentication.
# @POST Provisions user JIT and returns session token.
# @SIDE_EFFECT Provisions user in DB, creates auth session, writes security event log.
# @RELATION CALLS -> [AuthService]
# #region Api.Auth.CallbackAdfs [C:4] [TYPE Function] [SEMANTICS api,auth,adfs,callback]
# @ingroup Auth
# @BRIEF Handle ADFS OIDC callback — provisions user JIT, returns session token.
# @POST Provisions user in DB (JIT), creates auth session.
# @SIDE_EFFECT DB write for user provisioning; writes security event LOGIN_ADFS.
# @SIDE_EFFECT Molecular CoT: REASON/REFLECT/EXPLORE markers.
# @RELATION CALLS -> [Auth.Service]
# @TEST_EDGE: adfs_timeout -> 504
# @TEST_EDGE: invalid_state -> 401
@router.get("/callback/adfs", name="auth_callback_adfs")
async def auth_callback_adfs(
@@ -174,5 +188,5 @@ async def auth_callback_adfs(
return auth_service.create_session(user)
# #endregion auth_callback_adfs
# #endregion AuthApi
# #endregion Api.Auth.CallbackAdfs
# #endregion Api.Auth

View File

@@ -1,4 +1,5 @@
# #region ApiRoutesModule [C:5] [TYPE Module] [SEMANTICS api, package, router, lazy, import]
# @defgroup Api Module group.
# @BRIEF Provide lazy route module loading to avoid heavyweight imports during tests.
# @LAYER API
# @RELATION CALLS -> [ApiRoutesGetAttr]
@@ -8,6 +9,7 @@
# @INVARIANT Only names listed in __all__ are importable via __getattr__.
# #region Route_Group_Contracts [C:3] [TYPE Block]
# @ingroup Api
# @BRIEF Declare the canonical route-module registry used by lazy imports and app router inclusion.
# @RELATION DEPENDS_ON -> [PluginsRouter]
# @RELATION DEPENDS_ON -> [TasksRouter]
@@ -45,6 +47,7 @@ __all__ = [
# #region ApiRoutesGetAttr [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lazily import route module by attribute name.
# @RELATION DEPENDS_ON -> [ApiRoutesModule]
# @PRE name is module candidate exposed in __all__.

View File

@@ -1,4 +1,5 @@
# #region AdminApi [C:5] [TYPE Module] [SEMANTICS fastapi, admin, api, rbac, user]
# @defgroup Api Module group.
#
# @BRIEF Admin API endpoints for user and role management.
# @LAYER API
@@ -35,6 +36,7 @@ from ...services.rbac_permission_catalog import (
)
# #region router [TYPE Variable]
# @ingroup Api
# @RELATION DEPENDS_ON -> fastapi.APIRouter
# @BRIEF APIRouter instance for admin routes.
router = APIRouter(prefix="/api/admin", tags=["admin"])
@@ -42,6 +44,7 @@ router = APIRouter(prefix="/api/admin", tags=["admin"])
# #region list_users [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all registered users.
# @PRE Current user has 'Admin' role.
# @POST Returns a list of UserSchema objects.
@@ -59,6 +62,7 @@ async def list_users(
# #region create_user [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Creates a new local user.
# @PRE Current user has 'Admin' role.
# @POST New user is created in the database.
@@ -97,6 +101,7 @@ async def create_user(
# #region update_user [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing user.
# @PRE Current user has 'Admin' role.
# @POST User record is updated in the database.
@@ -137,6 +142,7 @@ async def update_user(
# #region delete_user [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Deletes a user.
# @PRE Current user has 'Admin' role.
# @POST User record is removed from the database.
@@ -174,6 +180,7 @@ async def delete_user(
# #region list_roles [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all available roles.
# @RELATION CALLS -> [Role]
@router.get("/roles", response_model=list[RoleSchema])
@@ -188,6 +195,7 @@ async def list_roles(
# #region create_role [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Creates a new system role with associated permissions.
# @PRE Role name must be unique.
# @POST New Role record is created in auth.db.
@@ -225,6 +233,7 @@ async def create_role(
# #region update_role [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing role's metadata and permissions.
# @PRE role_id must be a valid existing role UUID.
# @POST Role record is updated in auth.db.
@@ -268,6 +277,7 @@ async def update_role(
# #region delete_role [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Removes a role from the system.
# @PRE role_id must be a valid existing role UUID.
# @POST Role record is removed from auth.db.
@@ -294,6 +304,7 @@ async def delete_role(
# #region list_permissions [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all available system permissions for assignment.
# @POST Returns a list of all PermissionSchema objects.
# @RELATION CALLS -> backend.src.core.auth.repository.AuthRepository.list_permissions
@@ -324,6 +335,7 @@ async def list_permissions(
# #region list_ad_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all AD Group to Role mappings.
# @RELATION CALLS -> ADGroupMapping
@router.get("/ad-mappings", response_model=list[ADGroupMappingSchema])
@@ -339,6 +351,7 @@ async def list_ad_mappings(
# #region create_ad_mapping [C:2] [TYPE Function]
# @ingroup Api
# @RELATION DEPENDS_ON -> [ADGroupMapping]
# @RELATION DEPENDS_ON -> [get_auth_db]
# @RELATION DEPENDS_ON -> [has_permission]

View File

@@ -1,4 +1,5 @@
# #region AdminApiKeyRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, admin, api_key, crud]
# @defgroup Api Module group.
# @BRIEF Admin API endpoints for API key management — list, generate (one-time reveal), and revoke.
# @LAYER API
# @RELATION DEPENDS_ON -> [APIKeyModel]
@@ -20,6 +21,7 @@ from ...dependencies import has_permission
from ...models.api_key import APIKey
# #region router [TYPE Variable]
# @ingroup Api
# @BRIEF APIRouter for admin API key management routes.
router = APIRouter(prefix="/api/admin/api-keys", tags=["admin", "api-keys"])
# #endregion router
@@ -76,6 +78,7 @@ class ApiKeyRevokeResponse(BaseModel):
# ── Routes ────────────────────────────────────────────────────
# #region list_api_keys [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF List all API keys — NEVER returns key_hash or raw_key.
# @PRE Requires admin:settings WRITE permission.
# @POST Returns list of ApiKeyListItem without sensitive fields.
@@ -103,6 +106,7 @@ async def list_api_keys(
# #region create_api_key [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Generate a new API key — returns raw key ONCE, never stored or retrievable again.
# @PRE Requires admin:settings WRITE permission. name is required, at least one permission.
# @POST Creates APIKey row with SHA-256 hash. Returns raw key in response.
@@ -152,6 +156,7 @@ async def create_api_key(
# #region revoke_api_key [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Revoke an API key by setting active=False. Preserves row for audit.
# @PRE Requires admin:settings WRITE permission.
# @POST Sets active=False on the key. Returns 404 if already revoked or not found.

View File

@@ -1,4 +1,5 @@
# #region AssistantApi [C:5] [TYPE Module] [SEMANTICS assistant, api, package, llm, execution]
# @defgroup AssistantApi Module group.
# @BRIEF API routes for LLM assistant command parsing and safe execution orchestration.
# @LAYER API
# @RELATION DEPENDS_ON -> [TaskManager]

View File

@@ -1,4 +1,5 @@
# #region AssistantAdminRoutes [C:5] [TYPE Module] [SEMANTICS assistant, admin, route, audit, conversation]
# @defgroup AssistantApi Module group.
# @BRIEF FastAPI route handlers for assistant admin operations — conversation listing, deletion, history, audit.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantRoutes]
@@ -38,6 +39,7 @@ from ._schemas import (
# #region list_conversations [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Return paginated conversation list for current user with archived flag and last message preview.
# @PRE Authenticated user context and valid pagination params.
# @POST Conversations are grouped by conversation_id sorted by latest activity descending.
@@ -134,6 +136,7 @@ async def list_conversations(
# #region delete_conversation [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Soft-delete or hard-delete a conversation and clear its in-memory trace.
# @PRE conversation_id belongs to current_user.
# @POST Conversation records are removed from DB and CONVERSATIONS cache.
@@ -180,6 +183,7 @@ async def delete_conversation(
@router.get("/history")
# #region get_history [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Retrieve paginated assistant conversation history for current user.
# @PRE Authenticated user is available and page params are valid.
# @POST Returns persistent messages and mirrored in-memory snapshot for diagnostics.
@@ -251,6 +255,7 @@ async def get_history(
@router.get("/audit")
# #region get_assistant_audit [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Return assistant audit decisions for current user from persistent and in-memory stores.
# @PRE User has tasks:READ permission.
# @POST Audit payload is returned in reverse chronological order from DB.

View File

@@ -1,4 +1,5 @@
# #region AssistantCommandParser [C:4] [TYPE Module] [SEMANTICS assistant, command, parser, nlu, intent]
# @defgroup AssistantApi Module group.
# @BRIEF Deterministic RU/EN command text parser that converts user messages into intent payloads.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantResolvers]

View File

@@ -1,4 +1,5 @@
# #region AssistantDatasetReview [C:4] [TYPE Module] [SEMANTICS assistant, dataset, review, context, intent]
# @defgroup AssistantApi Module group.
# @BRIEF Dataset review context loading and intent planning for the assistant API.
# @LAYER API
# @RELATION DEPENDS_ON -> [DatasetReviewOrchestrator]

View File

@@ -1,4 +1,5 @@
# #region AssistantDatasetReviewDispatch [C:4] [TYPE Module] [SEMANTICS assistant, dataset, review, dispatch, confirm]
# @defgroup AssistantApi Module group.
# @BRIEF Dispatch and confirmation handling for dataset-review assistant intents.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantDatasetReview]

View File

@@ -1,4 +1,5 @@
# #region AssistantDispatch [C:4] [TYPE Module] [SEMANTICS assistant, dispatch, confirm, execution, orchestration]
# @defgroup AssistantApi Module group.
# @BRIEF Intent dispatch engine and backward-compat wrapper around the central tool registry.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]

View File

@@ -1,4 +1,5 @@
# #region AssistantHistory [C:2] [TYPE Module] [SEMANTICS assistant, history, audit, persistence, conversation]
# @defgroup AssistantApi Module group.
# @BRIEF Conversation history, audit trail, and confirmation persistence helpers for the assistant API.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantSchemas]

View File

@@ -1,4 +1,5 @@
# #region AssistantLlmPlanner [C:5] [TYPE Module] [SEMANTICS assistant, llm, planner, tool, catalog]
# @defgroup AssistantApi Module group.
# @BRIEF LLM-based intent planning, tool catalog construction, and authorization for the assistant API.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantSchemas]

View File

@@ -1,4 +1,5 @@
# #region AssistantLlmPlannerIntent [C:5] [TYPE Module] [SEMANTICS assistant, llm, intent, planning, authorization]
# @defgroup AssistantApi Module group.
# @BRIEF LLM-based intent planning and authorization for the assistant API — separated from tool catalog.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantLlmPlanner]

View File

@@ -1,4 +1,5 @@
# #region AssistantResolvers [C:2] [TYPE Module] [SEMANTICS assistant, resolver, lookup, environment, mapper]
# @defgroup AssistantApi Module group.
# @BRIEF Environment, dashboard, provider, and task resolution utilities for the assistant API.
# @LAYER API
# @RELATION DEPENDS_ON -> [ConfigManager]

View File

@@ -1,4 +1,5 @@
# #region AssistantRoutes [C:5] [TYPE Module] [SEMANTICS assistant, api, route, chat, execution]
# @defgroup AssistantApi Module group.
# @BRIEF FastAPI route handlers for the assistant API — message sending, confirmation, conversation management.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantSchemas]
@@ -65,6 +66,7 @@ router = APIRouter(tags=["Assistant"])
@router.post("/messages", response_model=AssistantMessageResponse)
# #region send_message [C:5] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Parse assistant command, enforce safety gates, and dispatch executable intent.
# @DATA_CONTRACT Input[AssistantMessageRequest,User,TaskManager,ConfigManager,Session] -> Output[AssistantMessageResponse]
# @RELATION DEPENDS_ON -> [_plan_intent_with_llm]
@@ -163,6 +165,7 @@ async def send_message(request: AssistantMessageRequest, current_user: User=Depe
"/confirmations/{confirmation_id}/confirm", response_model=AssistantMessageResponse
)
# #region confirm_operation [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Execute previously requested risky operation after explicit user confirmation.
# @PRE confirmation_id exists, belongs to current user, is pending, and not expired.
# @POST Confirmation state becomes consumed and operation result is persisted in history.
@@ -251,6 +254,7 @@ async def confirm_operation(
"/confirmations/{confirmation_id}/cancel", response_model=AssistantMessageResponse
)
# #region cancel_operation [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Cancel pending risky operation and mark confirmation token as cancelled.
# @PRE confirmation_id exists, belongs to current user, and is still pending.
# @POST Confirmation becomes cancelled and cannot be executed anymore.

View File

@@ -1,4 +1,5 @@
# #region AssistantSchemas [C:2] [TYPE Module] [SEMANTICS assistant, pydantic, schema, store, permission]
# @defgroup AssistantApi Module group.
# @BRIEF Pydantic models, in-memory stores, and permission mappings for the assistant API.
# @LAYER API
# @RELATION CALLED_BY -> [AssistantHistory]

View File

@@ -1,4 +1,5 @@
# #region AssistantToolBackup [C:3] [TYPE Module] [SEMANTICS assistant, tool, backup]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "run_backup" tool — run backup for environment or specific dashboard.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -26,6 +27,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_run_backup [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="run_backup",
domain="backup",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolCapabilities [C:3] [TYPE Module] [SEMANTICS assistant, tool, capabilities, catalog]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "show_capabilities" tool — lists available assistant commands and examples.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -31,6 +32,7 @@ _HUMAN_LABELS: dict[str, str] = {
# #region handle_show_capabilities [C:2] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="show_capabilities",
domain="assistant",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolCommit [C:3] [TYPE Module] [SEMANTICS assistant, tool, git, commit]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "commit_changes" tool — commit dashboard repository changes.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -24,6 +25,7 @@ from ._dispatch import _get_git_service
# #region handle_commit_changes [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="commit_changes",
domain="git",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolCreateBranch [C:3] [TYPE Module] [SEMANTICS assistant, tool, git, branch]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "create_branch" tool — create git branch for a dashboard.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -24,6 +25,7 @@ from ._dispatch import _get_git_service
# #region handle_create_branch [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="create_branch",
domain="git",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolDeploy [C:3] [TYPE Module] [SEMANTICS assistant, tool, git, deploy]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "deploy_dashboard" tool — deploy dashboard to target environment.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -22,6 +23,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_deploy_dashboard [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="deploy_dashboard",
domain="git",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolHealthSummary [C:3] [TYPE Module] [SEMANTICS assistant, tool, health, summary]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "get_health_summary" tool — get summary of dashboard health.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -22,6 +23,7 @@ from ._tool_registry import assistant_tool
# #region handle_get_health_summary [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="get_health_summary",
domain="health",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolListEnvironments [C:2] [TYPE Module] [SEMANTICS assistant, tool, environments, list]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "list_environments" tool — show available Superset environments.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -19,6 +20,7 @@ from ._tool_registry import assistant_tool
# #region handle_list_environments [C:2] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="list_environments",
domain="environments",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolLlm [C:2] [TYPE Module] [SEMANTICS assistant, tool, llm, providers, status]
# @defgroup AssistantApi Module group.
# @BRIEF Handlers for LLM operations — list providers and check LLM status.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -21,6 +22,7 @@ from ._tool_registry import assistant_tool
# #region handle_list_llm_providers [C:2] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="list_llm_providers",
domain="llm",
@@ -69,6 +71,7 @@ async def handle_list_llm_providers(
# #region handle_get_llm_status [C:2] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="get_llm_status",
domain="llm",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolLlmDocumentation [C:3] [TYPE Module] [SEMANTICS assistant, tool, llm, documentation]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "run_llm_documentation" tool — generate dataset documentation via LLM.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -22,6 +23,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_run_llm_documentation [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="run_llm_documentation",
domain="llm",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolLlmValidation [C:3] [TYPE Module] [SEMANTICS assistant, tool, llm, validation, task]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "run_llm_validation" tool — create a validation task (policy) and trigger a run.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -35,6 +36,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_run_llm_validation [C:4] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="run_llm_validation",
domain="llm",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolMaintenance [C:3] [TYPE Module] [SEMANTICS assistant, tool, maintenance, events]
# @defgroup AssistantApi Module group.
# @BRIEF Handlers for maintenance operations — list events, start maintenance, end maintenance.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -28,6 +29,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_list_maintenance_events [C:2] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="list_maintenance_events",
domain="maintenance",
@@ -151,6 +153,7 @@ async def handle_list_maintenance_events(
# #region handle_start_maintenance [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="start_maintenance",
domain="maintenance",
@@ -254,6 +257,7 @@ async def handle_start_maintenance(
# #region handle_end_maintenance [C:2] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="end_maintenance",
domain="maintenance",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolMigration [C:4] [TYPE Module] [SEMANTICS assistant, tool, migration, execute]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "execute_migration" tool — run dashboard migration between environments.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -27,6 +28,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_execute_migration [C:4] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="execute_migration",
domain="migration",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolRegistry [C:4] [TYPE Module] [SEMANTICS assistant, registry, tool, dispatch, catalog]
# @defgroup AssistantApi Module group.
# @BRIEF Central `@assistant_tool` decorator-based registry: auto-generates LLM catalog, dispatches operations,
# and enforces permissions. Replaces hand-maintained `_build_tool_catalog()`, `_dispatch_intent()`,
# `INTENT_PERMISSION_CHECKS`, and `_SAFE_OPS` to eliminate cross-file desynchronisation.
@@ -69,6 +70,7 @@ _tools: dict[str, AssistantTool] = {}
# #region assistant_tool [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Decorator that registers a function as an assistant tool handler.
# @POST Tool is registered in `_tools` by operation name. Handler gets `__assistant_tool__` attr.
# @SIDE_EFFECT Mutates the module-level `_tools` dict.
@@ -196,6 +198,7 @@ def get_permission_checks() -> dict[str, list[tuple[str, str]]]:
# #region get_catalog [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Build LLM tool catalog from registry, filtered by user permissions.
# @POST Returns list of dicts suitable for LLM planner prompt.
def get_catalog(
@@ -232,6 +235,7 @@ def get_catalog(
# #region dispatch [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Look up operation in registry and call its handler.
# @PRE operation is a known registered tool.
# @POST Returns (text, task_id, actions) from the handler.

View File

@@ -1,4 +1,5 @@
# #region AssistantToolSearchDashboards [C:3] [TYPE Module] [SEMANTICS assistant, tool, dashboards, search]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "search_dashboards" tool — search and list dashboards from an environment.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -22,6 +23,7 @@ from ._tool_registry import assistant_tool
# #region handle_search_dashboards [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="search_dashboards",
domain="dashboards",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolTaskStatus [C:3] [TYPE Module] [SEMANTICS assistant, tool, status, task]
# @defgroup AssistantApi Module group.
# @BRIEF Handler for the "get_task_status" tool — retrieve task status by id or latest user task.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -22,6 +23,7 @@ from ._tool_registry import _check_any_permission, assistant_tool
# #region handle_get_task_status [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool(
operation="get_task_status",
domain="status",

View File

@@ -1,4 +1,5 @@
# #region CleanReleaseApi [C:4] [TYPE Module] [SEMANTICS fastapi, clean-release, api, compliance, candidate, release]
# @defgroup Api Module group.
# @BRIEF Expose clean release endpoints for candidate preparation and subsequent compliance flow.
# @LAYER API
# @RELATION DEPENDS_ON -> [get_clean_release_repository]
@@ -47,6 +48,7 @@ router = APIRouter(prefix="/api/clean-release", tags=["Clean Release"])
# #region PrepareCandidateRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for candidate preparation endpoint.
class PrepareCandidateRequest(BaseModel):
candidate_id: str = Field(min_length=1)
@@ -59,6 +61,7 @@ class PrepareCandidateRequest(BaseModel):
# #region StartCheckRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for clean compliance check run startup.
class StartCheckRequest(BaseModel):
candidate_id: str = Field(min_length=1)
@@ -71,6 +74,7 @@ class StartCheckRequest(BaseModel):
# #region RegisterCandidateRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for candidate registration endpoint.
class RegisterCandidateRequest(BaseModel):
id: str = Field(min_length=1)
@@ -83,6 +87,7 @@ class RegisterCandidateRequest(BaseModel):
# #region ImportArtifactsRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for candidate artifact import endpoint.
class ImportArtifactsRequest(BaseModel):
artifacts: list[dict[str, Any]] = Field(default_factory=list)
@@ -92,6 +97,7 @@ class ImportArtifactsRequest(BaseModel):
# #region BuildManifestRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for manifest build endpoint.
class BuildManifestRequest(BaseModel):
created_by: str = Field(default="system")
@@ -101,6 +107,7 @@ class BuildManifestRequest(BaseModel):
# #region CreateComplianceRunRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for compliance run creation with optional manifest pinning.
class CreateComplianceRunRequest(BaseModel):
requested_by: str = Field(min_length=1)
@@ -111,6 +118,7 @@ class CreateComplianceRunRequest(BaseModel):
# #region register_candidate_v2_endpoint [TYPE Function]
# @ingroup Api
# @BRIEF Register a clean-release candidate for headless lifecycle.
# @PRE Candidate identifier is unique.
# @POST Candidate is persisted in DRAFT status.
@@ -152,6 +160,7 @@ async def register_candidate_v2_endpoint(
# #region import_candidate_artifacts_v2_endpoint [TYPE Function]
# @ingroup Api
# @BRIEF Import candidate artifacts in headless flow.
# @PRE Candidate exists and artifacts array is non-empty.
# @POST Artifacts are persisted and candidate advances to PREPARED if it was DRAFT.
@@ -210,6 +219,7 @@ async def import_candidate_artifacts_v2_endpoint(
# #region build_candidate_manifest_v2_endpoint [TYPE Function]
# @ingroup Api
# @BRIEF Build immutable manifest snapshot for prepared candidate.
# @PRE Candidate exists and has imported artifacts.
# @POST Returns created ManifestDTO with incremented version.
@@ -254,6 +264,7 @@ async def build_candidate_manifest_v2_endpoint(
# #region get_candidate_overview_v2_endpoint [TYPE Function]
# @ingroup Api
# @BRIEF Return expanded candidate overview DTO for headless lifecycle visibility.
# @PRE Candidate exists.
# @POST Returns CandidateOverviewDTO built from the same repository state used by headless US1 endpoints.
@@ -370,6 +381,7 @@ async def get_candidate_overview_v2_endpoint(
# #region prepare_candidate_endpoint [TYPE Function]
# @ingroup Api
# @BRIEF Prepare candidate with policy evaluation and deterministic manifest generation.
# @PRE Candidate and active policy exist in repository.
# @POST Returns preparation result including manifest reference and violations.
@@ -404,6 +416,7 @@ async def prepare_candidate_endpoint(
# #region start_check [TYPE Function]
# @ingroup Api
# @BRIEF Start and finalize a clean compliance check run and persist report artifacts.
# @PRE Active policy and candidate exist.
# @POST Returns accepted payload with check_run_id and started_at.
@@ -540,6 +553,7 @@ async def start_check(
# #region get_check_status [TYPE Function]
# @ingroup Api
# @BRIEF Return terminal/intermediate status payload for a check run.
# @PRE check_run_id references an existing run.
# @POST Deterministic payload shape includes checks and violations arrays.
@@ -592,6 +606,7 @@ async def get_check_status(
# #region get_report [TYPE Function]
# @ingroup Api
# @BRIEF Return persisted compliance report by report_id.
# @PRE report_id references an existing report.
# @POST Returns serialized report object.

View File

@@ -1,4 +1,5 @@
# #region CleanReleaseV2Api [C:4] [TYPE Module] [SEMANTICS fastapi, clean-release, candidate, lifecycle, api]
# @defgroup Api Module group.
# @BRIEF Redesigned clean release API for headless candidate lifecycle.
# @LAYER API
# @RELATION DEPENDS_ON -> [CleanReleaseRepository]
@@ -62,6 +63,7 @@ class RevokeRequest(dict):
# #region register_candidate [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Register a new release candidate.
# @PRE Payload contains required fields (id, version, source_snapshot_ref, created_by).
# @POST Candidate is saved in repository.
@@ -97,6 +99,7 @@ async def register_candidate(
# #region import_artifacts [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Associate artifacts with a release candidate.
# @PRE Candidate exists.
# @POST Artifacts are processed (placeholder).
@@ -130,6 +133,7 @@ async def import_artifacts(
# #region build_manifest [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Generate distribution manifest for a candidate.
# @PRE Candidate exists.
# @POST Manifest is created and saved.
@@ -177,6 +181,7 @@ async def build_manifest(
# #region approve_candidate_endpoint [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Endpoint to record candidate approval.
# @RELATION CALLS -> [approve_candidate]
@router.post("/candidates/{candidate_id}/approve")
@@ -205,6 +210,7 @@ async def approve_candidate_endpoint(
# #region reject_candidate_endpoint [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Endpoint to record candidate rejection.
# @RELATION CALLS -> [reject_candidate]
@router.post("/candidates/{candidate_id}/reject")
@@ -233,6 +239,7 @@ async def reject_candidate_endpoint(
# #region publish_candidate_endpoint [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Endpoint to publish an approved candidate.
# @RELATION CALLS -> [publish_candidate]
@router.post("/candidates/{candidate_id}/publish")
@@ -277,6 +284,7 @@ async def publish_candidate_endpoint(
# #region revoke_publication_endpoint [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Endpoint to revoke a previous publication.
# @RELATION CALLS -> [revoke_publication]
@router.post("/publications/{publication_id}/revoke")

View File

@@ -1,4 +1,5 @@
# #region DashboardsApi [C:5] [TYPE Module] [SEMANTICS dashboard, api, package, search, git, task]
# @defgroup Api Module group.
#
# @BRIEF API endpoints for the Dashboard Hub - listing dashboards with Git and task status
# @LAYER API

View File

@@ -1,4 +1,5 @@
# #region DashboardActionRoutes [C:2] [TYPE Module] [SEMANTICS fastapi, dashboard, api, backup]
# @defgroup Api Module group.
# @BRIEF Dashboard action route handlers — migrate, backup.
# @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]]
@@ -22,6 +23,7 @@ from ._schemas import (
# #region migrate_dashboards [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk migration of dashboards from source to target environment
# @PRE User has permission plugin:migration:execute
# @PRE source_env_id and target_env_id are valid environment IDs
@@ -100,6 +102,7 @@ async def migrate_dashboards(
# #region backup_dashboards [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk backup of dashboards with optional cron schedule
# @PRE User has permission plugin:backup:execute
# @PRE env_id is a valid environment ID

View File

@@ -1,4 +1,5 @@
# #region DashboardDetailRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, dashboard, api, transform, search, task]
# @defgroup Api Module group.
# @BRIEF Dashboard detail, db-mappings, task history, thumbnail route handlers.
# @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]]
@@ -41,6 +42,7 @@ from ._schemas import (
# #region get_database_mappings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get database mapping suggestions between source and target environments
# @PRE User has permission plugin:migration:read
# @PRE source_env_id and target_env_id are valid environment IDs
@@ -107,6 +109,7 @@ async def get_database_mappings(
# #region get_dashboard_detail [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch detailed dashboard info with related charts and datasets
# @PRE env_id must be valid and dashboard ref (slug or id) must exist
# @POST Returns dashboard detail payload for overview page
@@ -166,6 +169,7 @@ async def get_dashboard_detail(
# #region get_dashboard_tasks_history [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Returns history of backup and LLM validation tasks for a dashboard.
# @PRE dashboard ref (slug or id) is valid.
# @POST Response contains sorted task history (newest first).
@@ -268,6 +272,7 @@ async def get_dashboard_tasks_history(
# #region get_dashboard_thumbnail [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Proxies Superset dashboard thumbnail with cache support.
# @RELATION CALLS -> [AsyncAPIClient]
# @PRE env_id must exist.

View File

@@ -1,4 +1,5 @@
# #region DashboardHelpers [C:2] [TYPE Module] [SEMANTICS fastapi, dashboard, api, search, filter]
# @defgroup Api Module group.
# @BRIEF Basic helper functions for dashboard route handlers — slug resolution, filter normalization.
# @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [SupersetClient]

View File

@@ -1,4 +1,5 @@
# #region DashboardListingRoutes [C:4] [TYPE Module] [SEMANTICS fastapi, dashboard, api, search]
# @defgroup Api Module group.
# @BRIEF Dashboard listing route handler for Dashboard Hub.
# @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]]
@@ -35,6 +36,7 @@ from ._schemas import DashboardsResponse, EffectiveProfileFilter
# #region get_dashboards [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch list of dashboards from a specific environment with Git status and last task status
# @PRE env_id must be a valid environment ID
# @PRE page must be >= 1 if provided

View File

@@ -1,4 +1,5 @@
# #region DashboardProjection [C:2] [TYPE Module] [SEMANTICS dashboard, api, transform, profile, filter]
# @defgroup Api Module group.
# @BRIEF Dashboard response projection and profile-filter helpers for Dashboard Hub routes.
# @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [SupersetClient]

View File

@@ -21,6 +21,7 @@ class GitStatus(BaseModel):
# #region LastTask [C:2] [TYPE DataClass]
# @ingroup Api
# @BRIEF DTO for the most recent background task associated with a dashboard.
class LastTask(BaseModel):
task_id: str | None = None
@@ -154,6 +155,7 @@ class DashboardTaskHistoryResponse(BaseModel):
# #region DatabaseMapping [C:2] [TYPE DataClass]
# @ingroup Api
# @BRIEF DTO for cross-environment database ID mapping.
class DatabaseMapping(BaseModel):
source_db: str

View File

@@ -1,4 +1,5 @@
# #region DatasetReviewApi [C:3] [TYPE Module] [SEMANTICS dataset, review, api, facade]
# @defgroup Api Module group.
# @BRIEF Thin facade re-exporting router and public symbols from decomposed dataset review API sub-modules.
# @LAYER API
# @RATIONALE Original 2484-line monolith violated INV_7 (400-line module limit) by 6x. Decomposed into _dependencies (DTOs/guards/serializers) and _routes (handlers).

View File

@@ -1,4 +1,5 @@
# #region DatasetReviewDependencies [C:2] [TYPE Module] [SEMANTICS dataset, review, dependency, di, serialize]
# @defgroup Api Module group.
# @BRIEF Dependency injection, serialization helpers, and feature-flag guards for dataset review endpoints.
# @LAYER API

View File

@@ -1,4 +1,5 @@
# #region DatasetReviewRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, dataset, review, feedback, route]
# @defgroup Api Module group.
# @BRIEF Persist thumbs up/down feedback for clarification question/answer content.
@@ -92,6 +93,7 @@ router = APIRouter(prefix="/api/dataset-orchestration", tags=["Dataset Orchestra
# #region list_sessions [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List resumable dataset review sessions for the current user.
@router.get(
"/sessions",
@@ -137,6 +139,7 @@ async def list_sessions(
# #region start_session [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Start a new dataset review session from a Superset link or dataset selection.
@router.post(
"/sessions",
@@ -191,6 +194,7 @@ async def start_session(
# #region get_session_detail [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Return the full accessible dataset review session aggregate.
@router.get(
"/sessions/{session_id}",
@@ -214,6 +218,7 @@ async def get_session_detail(
# #region update_session [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Update resumable lifecycle status for an owned session.
@router.patch(
"/sessions/{session_id}",
@@ -263,6 +268,7 @@ async def update_session(
# #region delete_session [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Archive or hard-delete a session owned by the current user.
@router.delete(
"/sessions/{session_id}",
@@ -314,6 +320,7 @@ async def delete_session(
# #region export_documentation [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Export documentation output for the current session.
@router.get(
"/sessions/{session_id}/exports/documentation",
@@ -352,6 +359,7 @@ async def export_documentation(
# #region export_validation [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Export validation findings for the current session.
@router.get(
"/sessions/{session_id}/exports/validation",
@@ -390,6 +398,7 @@ async def export_validation(
# #region get_clarification_state [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Return the current clarification session summary and active question payload.
@router.get(
"/sessions/{session_id}/clarification",
@@ -426,6 +435,7 @@ async def get_clarification_state(
# #region resume_clarification [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Resume clarification mode on the highest-priority unresolved question.
@router.post(
"/sessions/{session_id}/clarification/resume",
@@ -463,6 +473,7 @@ async def resume_clarification(
# #region record_clarification_answer [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist one clarification answer before advancing the active pointer.
@router.post(
"/sessions/{session_id}/clarification/answers",
@@ -513,6 +524,7 @@ async def record_clarification_answer(
# #region update_field_semantic [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Apply one field-level semantic candidate decision or manual override.
@router.patch(
"/sessions/{session_id}/fields/{field_id}/semantic",
@@ -553,6 +565,7 @@ async def update_field_semantic(
# #region lock_field_semantic [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lock one semantic field against later automatic overwrite.
@router.post(
"/sessions/{session_id}/fields/{field_id}/lock",
@@ -593,6 +606,7 @@ async def lock_field_semantic(
# #region unlock_field_semantic [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Unlock one semantic field so later automated candidate application may replace it.
@router.post(
"/sessions/{session_id}/fields/{field_id}/unlock",
@@ -636,6 +650,7 @@ async def unlock_field_semantic(
# #region approve_batch_semantic_fields [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Approve multiple semantic candidate decisions in one batch.
@router.post(
"/sessions/{session_id}/fields/semantic/approve-batch",
@@ -686,6 +701,7 @@ async def approve_batch_semantic_fields(
# #region list_execution_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Return the current mapping-review set for one accessible session.
@router.get(
"/sessions/{session_id}/mappings",
@@ -712,6 +728,7 @@ async def list_execution_mappings(
# #region update_execution_mapping [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist one owner-authorized execution-mapping effective value override.
@router.patch(
"/sessions/{session_id}/mappings/{mapping_id}",
@@ -777,6 +794,7 @@ async def update_execution_mapping(
# #region approve_execution_mapping [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Explicitly approve a warning-sensitive mapping transformation.
@router.post(
"/sessions/{session_id}/mappings/{mapping_id}/approve",
@@ -825,6 +843,7 @@ async def approve_execution_mapping(
# #region approve_batch_execution_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Approve multiple warning-sensitive execution mappings in one batch.
@router.post(
"/sessions/{session_id}/mappings/approve-batch",
@@ -877,6 +896,7 @@ async def approve_batch_execution_mappings(
# #region trigger_preview_generation [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger Superset-side preview compilation for the current owned execution context.
@router.post(
"/sessions/{session_id}/preview",
@@ -938,6 +958,7 @@ async def trigger_preview_generation(
# #region launch_dataset [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Execute the current owned session launch handoff and return audited SQL Lab run context.
@router.post(
"/sessions/{session_id}/launch",
@@ -996,6 +1017,7 @@ async def launch_dataset(
# #region record_field_feedback [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist thumbs up/down feedback for AI-assisted semantic field content.
@router.post(
"/sessions/{session_id}/fields/{field_id}/feedback",
@@ -1040,6 +1062,7 @@ async def record_field_feedback(
# #region record_clarification_feedback [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist thumbs up/down feedback for clarification question/answer content.
@router.post(
"/sessions/{session_id}/clarification/questions/{question_id}/feedback",

View File

@@ -1,4 +1,5 @@
# #region DatasetsApi [C:5] [TYPE Module] [SEMANTICS fastapi, dataset, api, search, mapping, mapped-fields]
# @defgroup Api Module group.
#
# @BRIEF API endpoints for the Dataset Hub - listing datasets with mapping progress
# @LAYER API
@@ -84,6 +85,7 @@ class MetricItem(BaseModel):
# #endregion MetricItem
# #region DatasetDetailResponse [C:2] [TYPE DataClass]
# @ingroup Api
# @BRIEF Detailed DTO for a dataset including columns, linked dashboards, and metrics.
# @RELATION DEPENDS_ON -> [MetricItem]
class DatasetDetailResponse(BaseModel):
@@ -116,6 +118,7 @@ class StatsCounts(BaseModel):
# #endregion StatsCounts
# #region DatasetsResponse [C:2] [TYPE DataClass]
# @ingroup Api
# @BRIEF Paginated response DTO for dataset listings with stats.
# @RELATION DEPENDS_ON -> [StatsCounts]
class DatasetsResponse(BaseModel):
@@ -146,6 +149,7 @@ class MetricDescriptionUpdate(BaseModel):
# #endregion MetricDescriptionUpdate
# #region get_dataset_ids [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch list of all dataset IDs from a specific environment (without pagination)
# @PRE env_id must be a valid environment ID
# @POST Returns a list of all dataset IDs
@@ -194,6 +198,7 @@ async def get_dataset_ids(
# #endregion get_dataset_ids
# #region get_datasets [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch list of datasets from a specific environment with mapping progress and stats.
# @PRE env_id must be a valid environment ID
# @PRE page must be >= 1 if provided
@@ -321,6 +326,7 @@ class MapColumnsRequest(BaseModel):
# #endregion MapColumnsRequest
# #region map_columns [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk column mapping for datasets
# @PRE User has permission plugin:mapper:execute
# @PRE env_id is a valid environment ID
@@ -394,6 +400,7 @@ class GenerateDocsRequest(BaseModel):
# #endregion GenerateDocsRequest
# #region generate_docs [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk documentation generation for datasets
# @PRE User has permission plugin:llm_analysis:execute
# @PRE env_id is a valid environment ID
@@ -447,6 +454,7 @@ async def generate_docs(
# #endregion generate_docs
# #region get_dataset_detail [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get detailed dataset information including columns and linked dashboards
# @PRE env_id is a valid environment ID
# @PRE dataset_id is a valid dataset ID
@@ -502,6 +510,7 @@ def _strip_html_tags(text: str) -> str:
# #endregion _strip_html_tags
# #region update_column_description [C:4] [TYPE Endpoint]
# @ingroup Api
# @BRIEF Save description for a single dataset column. Internal: GET full dataset from Superset, modify one column's description, PUT back.
# @PRE dataset_id and column_id must exist in the target environment.
# @POST Column description in Superset is updated. Response confirms success.
@@ -591,6 +600,7 @@ async def update_column_description(
# #endregion update_column_description
# #region update_metric_description [C:4] [TYPE Endpoint]
# @ingroup Api
# @BRIEF Save description for a single dataset metric. Mirror of update_column_description for metrics.
# @PRE dataset_id and metric_id must exist in the target environment.
# @POST Metric description in Superset is updated.

View File

@@ -1,4 +1,5 @@
# #region EnvironmentsApi [C:5] [TYPE Module] [SEMANTICS fastapi, environment, api]
# @defgroup Api Module group.
#
# @BRIEF API endpoints for listing environments and their databases.
# @LAYER API
@@ -30,12 +31,14 @@ def _normalize_superset_env_url(raw_url: str) -> str:
# #endregion _normalize_superset_env_url
# #region ScheduleSchema [TYPE DataClass]
# @ingroup Api
class ScheduleSchema(BaseModel):
enabled: bool = False
cron_expression: str = Field(..., pattern=r'^(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|((((\d+,)*\d+|(\d+(\/|-)\d+)|\d+|\*) ?){4,6})$')
# #endregion ScheduleSchema
# #region EnvironmentResponse [TYPE DataClass]
# @ingroup Api
class EnvironmentResponse(BaseModel):
id: str
name: str
@@ -46,6 +49,7 @@ class EnvironmentResponse(BaseModel):
# #endregion EnvironmentResponse
# #region DatabaseResponse [TYPE DataClass]
# @ingroup Api
class DatabaseResponse(BaseModel):
uuid: str
database_name: str
@@ -53,6 +57,7 @@ class DatabaseResponse(BaseModel):
# #endregion DatabaseResponse
# #region get_environments [TYPE Function] [SEMANTICS list, environments, config]
# @ingroup Api
# @BRIEF List all configured environments.
# @LAYER API
# @PRE config_manager is injected via Depends.
@@ -90,6 +95,7 @@ async def get_environments(
# #endregion get_environments
# #region update_environment_schedule [TYPE Function] [SEMANTICS update, schedule, backup, environment]
# @ingroup Api
# @BRIEF Update backup schedule for an environment.
# @LAYER API
# @PRE Environment id exists, schedule is valid ScheduleSchema.
@@ -121,6 +127,7 @@ async def update_environment_schedule(
# #endregion update_environment_schedule
# #region get_environment_databases [TYPE Function] [SEMANTICS fetch, databases, superset, environment]
# @ingroup Api
# @BRIEF Fetch the list of databases from a specific environment.
# @LAYER API
# @PRE Environment id exists.

View File

@@ -1,4 +1,5 @@
# #region GitPackage [C:5] [TYPE Module] [SEMANTICS git, api, package, sync]
# @defgroup Api Module group.
# @BRIEF Package root for decomposed git routes. Re-exports all public symbols from submodules.
# @LAYER API
# @RELATION CALLS -> [GitPackage]

View File

@@ -1,4 +1,5 @@
# #region GitConfigRoutes [C:2] [TYPE Module] [SEMANTICS fastapi, git, api, search, connection]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoints for Git server configuration CRUD and connection testing.
# @LAYER API
@@ -21,6 +22,7 @@ from ._router import router
# #region get_git_configs [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF List all configured Git servers.
@router.get("/config", response_model=list[GitServerConfigSchema])
async def get_git_configs(
@@ -39,6 +41,7 @@ async def get_git_configs(
# #region create_git_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Register a new Git server configuration.
@router.post("/config", response_model=GitServerConfigSchema)
async def create_git_config(
@@ -57,6 +60,7 @@ async def create_git_config(
# #region update_git_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing Git server configuration.
@router.put("/config/{config_id}", response_model=GitServerConfigSchema)
async def update_git_config(
@@ -89,6 +93,7 @@ async def update_git_config(
# #region delete_git_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Remove a Git server configuration.
@router.delete("/config/{config_id}")
async def delete_git_config(
@@ -110,6 +115,7 @@ async def delete_git_config(
# #region test_git_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Validate connection to a Git server using provided credentials.
@router.post("/config/test")
async def test_git_config(

View File

@@ -1,4 +1,5 @@
# #region GitEnvironmentRoutes [C:2] [TYPE Module] [SEMANTICS fastapi, git, environment, api]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoint for listing deployment environments.
# @LAYER API
@@ -13,6 +14,7 @@ from ._router import router
# #region get_environments [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF List all deployment environments.
@router.get("/environments", response_model=list[DeploymentEnvironmentSchema])
async def get_environments(

View File

@@ -1,4 +1,5 @@
# #region GitGiteaRoutes [C:2] [TYPE Module] [SEMANTICS fastapi, git, gitea, api]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoints for Gitea-specific repository operations.
# @LAYER API
@@ -23,6 +24,7 @@ from ._router import router
# #region list_gitea_repositories [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF List repositories in Gitea for a saved Gitea config.
@router.get("/config/{config_id}/gitea/repos", response_model=list[GiteaRepoSchema])
async def list_gitea_repositories(
@@ -54,6 +56,7 @@ async def list_gitea_repositories(
# #region create_gitea_repository [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Create a repository in Gitea for a saved Gitea config.
@router.post("/config/{config_id}/gitea/repos", response_model=GiteaRepoSchema)
async def create_gitea_repository(
@@ -91,6 +94,7 @@ async def create_gitea_repository(
# #region delete_gitea_repository [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Delete repository in Gitea for a saved Gitea config.
@router.delete("/config/{config_id}/gitea/repos/{owner}/{repo_name}")
async def delete_gitea_repository(
@@ -118,6 +122,7 @@ async def delete_gitea_repository(
# #region create_remote_repository [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Create repository on remote Git server using selected provider config.
@router.post("/config/{config_id}/repositories", response_model=RemoteRepoSchema)
async def create_remote_repository(

View File

@@ -1,4 +1,5 @@
# #region GitHelpers [C:3] [TYPE Module] [SEMANTICS fastapi, git, api]
# @defgroup Api Module group.
# @BRIEF Shared helper functions for Git route modules.
# @LAYER API
# @RELATION CALLS -> [GitDeps]

View File

@@ -1,4 +1,5 @@
# #region GitMergeRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, git, api]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoints for merge operations (status, conflicts, resolve, abort, continue).
# @LAYER API
@@ -22,6 +23,7 @@ from ._router import router
# #region get_merge_status [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return unfinished-merge status for repository (web-only recovery support).
@router.get(
"/repositories/{dashboard_ref}/merge/status", response_model=MergeStatusSchema
@@ -49,6 +51,7 @@ async def get_merge_status(
# #region get_merge_conflicts [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return conflicted files with mine/theirs previews for web conflict resolver.
@router.get(
"/repositories/{dashboard_ref}/merge/conflicts",
@@ -77,6 +80,7 @@ async def get_merge_conflicts(
# #region resolve_merge_conflicts [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Apply mine/theirs/manual conflict resolutions from WebUI and stage files.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/merge/resolve")
@@ -108,6 +112,7 @@ async def resolve_merge_conflicts(
# #region abort_merge [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Abort unfinished merge from WebUI flow.
@router.post("/repositories/{dashboard_ref}/merge/abort")
async def abort_merge(
@@ -133,6 +138,7 @@ async def abort_merge(
# #region continue_merge [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Finalize unfinished merge from WebUI flow.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/merge/continue")

View File

@@ -1,4 +1,5 @@
# #region GitRepoLifecycleRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, git, api, sync, deploy]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoints for Git lifecycle operations (sync, promote, deploy).
# @LAYER API
@@ -27,6 +28,7 @@ from ._router import router
# #region sync_dashboard [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Sync dashboard state from Superset to Git using the GitPlugin.
# @RELATION CALLS -> [GitPlugin]
@router.post("/repositories/{dashboard_ref}/sync")
@@ -62,6 +64,7 @@ async def sync_dashboard(
# #region promote_dashboard [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Promote changes between branches via MR or direct merge.
# @RELATION CALLS -> [GitPlugin]
@router.post("/repositories/{dashboard_ref}/promote", response_model=PromoteResponse)
@@ -185,6 +188,7 @@ async def promote_dashboard(
# #region deploy_dashboard [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Deploy dashboard from Git to a target environment.
# @RELATION CALLS -> [GitPlugin]
@router.post("/repositories/{dashboard_ref}/deploy")

View File

@@ -1,4 +1,5 @@
# #region GitRepoOperationsRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, git, api, history, diff]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoints for Git repository operations (commit, push, pull, status, diff, history).
# @LAYER API
@@ -29,6 +30,7 @@ from ._router import router
# #region commit_changes [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Stage and commit changes in the dashboard's repository.
@router.post("/repositories/{dashboard_ref}/commit")
async def commit_changes(
@@ -61,6 +63,7 @@ async def commit_changes(
# #region push_changes [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Push local commits to the remote repository.
@router.post("/repositories/{dashboard_ref}/push")
async def push_changes(
@@ -87,6 +90,7 @@ async def push_changes(
# #region pull_changes [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Pull changes from the remote repository.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/pull")
@@ -149,6 +153,7 @@ async def pull_changes(
# #region get_repository_status [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get current Git status for a dashboard repository.
@router.get("/repositories/{dashboard_ref}/status")
async def get_repository_status(
@@ -173,6 +178,7 @@ async def get_repository_status(
# #region get_repository_status_batch [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get Git statuses for multiple dashboard repositories in one request.
@router.post("/repositories/status/batch", response_model=RepoStatusBatchResponse)
async def get_repository_status_batch(
@@ -212,6 +218,7 @@ async def get_repository_status_batch(
# #region get_repository_diff [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get Git diff for a dashboard repository.
@router.get("/repositories/{dashboard_ref}/diff")
async def get_repository_diff(
@@ -239,6 +246,7 @@ async def get_repository_diff(
# #region get_history [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF View commit history for a dashboard's repository.
@router.get("/repositories/{dashboard_ref}/history", response_model=list[CommitSchema])
async def get_history(
@@ -265,6 +273,7 @@ async def get_history(
# #region generate_commit_message [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Generate a suggested commit message using LLM.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/generate-message")

View File

@@ -1,4 +1,5 @@
# #region GitRepoRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, git, api, search]
# @defgroup Api Module group.
# @BRIEF FastAPI endpoints for core Git repository operations (init, binding, branches, checkout).
# @LAYER API
@@ -29,6 +30,7 @@ from ._router import router
# #region init_repository [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Link a dashboard to a Git repository and perform initial clone/init.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/init")
@@ -109,6 +111,7 @@ async def init_repository(
# #region get_repository_binding [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return repository binding with provider metadata for selected dashboard.
@router.get("/repositories/{dashboard_ref}", response_model=RepositoryBindingSchema)
async def get_repository_binding(
@@ -150,6 +153,7 @@ async def get_repository_binding(
# #region delete_repository [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Delete local repository workspace and DB binding for selected dashboard.
@router.delete("/repositories/{dashboard_ref}")
async def delete_repository(
@@ -176,6 +180,7 @@ async def delete_repository(
# #region get_branches [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF List all branches for a dashboard's repository.
@router.get("/repositories/{dashboard_ref}/branches", response_model=list[BranchSchema])
async def get_branches(
@@ -201,6 +206,7 @@ async def get_branches(
# #region create_branch [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new branch in the dashboard's repository.
@router.post("/repositories/{dashboard_ref}/branches")
async def create_branch(
@@ -233,6 +239,7 @@ async def create_branch(
# #region checkout_branch [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Switch the dashboard's repository to a specific branch.
@router.post("/repositories/{dashboard_ref}/checkout")
async def checkout_branch(

View File

@@ -27,6 +27,7 @@ class GitServerConfigBase(BaseModel):
# #endregion GitServerConfigBase
# #region GitServerConfigUpdate [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for updating an existing Git server configuration.
class GitServerConfigUpdate(BaseModel):
name: str | None = Field(None, description="Display name for the Git server")
@@ -38,6 +39,7 @@ class GitServerConfigUpdate(BaseModel):
# #endregion GitServerConfigUpdate
# #region GitServerConfigCreate [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for creating a new Git server configuration.
class GitServerConfigCreate(GitServerConfigBase):
"""Schema for creating a new Git server configuration."""
@@ -45,6 +47,7 @@ class GitServerConfigCreate(GitServerConfigBase):
# #endregion GitServerConfigCreate
# #region GitServerConfigSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for representing a Git server configuration with metadata.
class GitServerConfigSchema(GitServerConfigBase):
"""Schema for representing a Git server configuration with metadata."""
@@ -56,6 +59,7 @@ class GitServerConfigSchema(GitServerConfigBase):
# #endregion GitServerConfigSchema
# #region GitRepositorySchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for tracking a local Git repository linked to a dashboard.
class GitRepositorySchema(BaseModel):
"""Schema for tracking a local Git repository linked to a dashboard."""
@@ -71,6 +75,7 @@ class GitRepositorySchema(BaseModel):
# #endregion GitRepositorySchema
# #region BranchSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for representing a Git branch metadata.
class BranchSchema(BaseModel):
"""Schema for representing a Git branch."""
@@ -81,6 +86,7 @@ class BranchSchema(BaseModel):
# #endregion BranchSchema
# #region CommitSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for representing Git commit details.
class CommitSchema(BaseModel):
"""Schema for representing a Git commit."""
@@ -93,6 +99,7 @@ class CommitSchema(BaseModel):
# #endregion CommitSchema
# #region BranchCreate [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for branch creation requests.
class BranchCreate(BaseModel):
"""Schema for branch creation requests."""
@@ -101,6 +108,7 @@ class BranchCreate(BaseModel):
# #endregion BranchCreate
# #region BranchCheckout [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for branch checkout requests.
class BranchCheckout(BaseModel):
"""Schema for branch checkout requests."""
@@ -108,6 +116,7 @@ class BranchCheckout(BaseModel):
# #endregion BranchCheckout
# #region CommitCreate [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for staging and committing changes.
class CommitCreate(BaseModel):
"""Schema for staging and committing changes."""
@@ -116,6 +125,7 @@ class CommitCreate(BaseModel):
# #endregion CommitCreate
# #region ConflictResolution [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for resolving merge conflicts.
class ConflictResolution(BaseModel):
"""Schema for resolving merge conflicts."""
@@ -126,6 +136,7 @@ class ConflictResolution(BaseModel):
# #region MergeStatusSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema representing unfinished merge status for repository.
class MergeStatusSchema(BaseModel):
has_unfinished_merge: bool
@@ -139,6 +150,7 @@ class MergeStatusSchema(BaseModel):
# #region MergeConflictFileSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema describing one conflicted file with optional side snapshots.
class MergeConflictFileSchema(BaseModel):
file_path: str
@@ -148,6 +160,7 @@ class MergeConflictFileSchema(BaseModel):
# #region MergeResolveRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for resolving one or multiple merge conflicts.
class MergeResolveRequest(BaseModel):
resolutions: list[ConflictResolution] = Field(default_factory=list)
@@ -155,12 +168,14 @@ class MergeResolveRequest(BaseModel):
# #region MergeContinueRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for finishing merge with optional explicit commit message.
class MergeContinueRequest(BaseModel):
message: str | None = None
# #endregion MergeContinueRequest
# #region DeploymentEnvironmentSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for representing a target deployment environment.
class DeploymentEnvironmentSchema(BaseModel):
"""Schema for representing a target deployment environment."""
@@ -173,6 +188,7 @@ class DeploymentEnvironmentSchema(BaseModel):
# #endregion DeploymentEnvironmentSchema
# #region DeployRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for dashboard deployment requests.
class DeployRequest(BaseModel):
"""Schema for deployment requests."""
@@ -180,6 +196,7 @@ class DeployRequest(BaseModel):
# #endregion DeployRequest
# #region RepoInitRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for repository initialization requests.
class RepoInitRequest(BaseModel):
"""Schema for repository initialization requests."""
@@ -189,6 +206,7 @@ class RepoInitRequest(BaseModel):
# #region RepositoryBindingSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema describing repository-to-config binding and provider metadata.
class RepositoryBindingSchema(BaseModel):
dashboard_id: int
@@ -199,6 +217,7 @@ class RepositoryBindingSchema(BaseModel):
# #endregion RepositoryBindingSchema
# #region RepoStatusBatchRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for requesting repository statuses for multiple dashboards in a single call.
class RepoStatusBatchRequest(BaseModel):
dashboard_ids: list[int] = Field(default_factory=list, description="Dashboard IDs to resolve repository statuses for")
@@ -206,6 +225,7 @@ class RepoStatusBatchRequest(BaseModel):
# #region RepoStatusBatchResponse [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema for returning repository statuses keyed by dashboard ID.
class RepoStatusBatchResponse(BaseModel):
statuses: dict[str, dict[str, Any]]
@@ -213,6 +233,7 @@ class RepoStatusBatchResponse(BaseModel):
# #region GiteaRepoSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Schema describing a Gitea repository.
class GiteaRepoSchema(BaseModel):
name: str
@@ -226,6 +247,7 @@ class GiteaRepoSchema(BaseModel):
# #region GiteaRepoCreateRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for creating a Gitea repository.
class GiteaRepoCreateRequest(BaseModel):
name: str = Field(..., min_length=1, max_length=255)
@@ -237,6 +259,7 @@ class GiteaRepoCreateRequest(BaseModel):
# #region RemoteRepoSchema [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Provider-agnostic remote repository payload.
class RemoteRepoSchema(BaseModel):
provider: GitProvider
@@ -251,6 +274,7 @@ class RemoteRepoSchema(BaseModel):
# #region RemoteRepoCreateRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Provider-agnostic repository creation request.
class RemoteRepoCreateRequest(BaseModel):
name: str = Field(..., min_length=1, max_length=255)
@@ -262,6 +286,7 @@ class RemoteRepoCreateRequest(BaseModel):
# #region PromoteRequest [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Request schema for branch promotion workflow.
class PromoteRequest(BaseModel):
from_branch: str = Field(..., min_length=1, max_length=255)
@@ -276,6 +301,7 @@ class PromoteRequest(BaseModel):
# #region PromoteResponse [TYPE Class]
# @defgroup Api Module group.
# @BRIEF Response schema for promotion operation result.
class PromoteResponse(BaseModel):
mode: str

View File

@@ -1,4 +1,5 @@
# #region health_router [C:3] [TYPE Module] [SEMANTICS fastapi, health, api, search, dashboard]
# @defgroup Api Module group.
# @BRIEF API endpoints for dashboard health monitoring and status aggregation.
# @LAYER UI
# @RELATION DEPENDS_ON -> [health_service]
@@ -15,6 +16,7 @@ from ...services.health_service import HealthService
router = APIRouter(prefix="/api/health", tags=["Health"])
# #region get_health_summary [TYPE Function]
# @ingroup Api
# @BRIEF Get aggregated health status for all dashboards.
# @PRE Caller has read permission for dashboard health view.
# @POST Returns HealthSummaryResponse.
@@ -38,6 +40,7 @@ async def get_health_summary(
# #region delete_health_report [TYPE Function]
# @ingroup Api
# @BRIEF Delete one persisted dashboard validation report from health summary.
# @PRE Caller has write permission for tasks/report maintenance.
# @POST Validation record is removed; linked task/logs are cleaned when available.

View File

@@ -1,4 +1,5 @@
# #region LlmRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, llm, api, provider]
# @defgroup Api Module group.
# @BRIEF API routes for LLM provider configuration and management.
# @LAYER API
# @RELATION DEPENDS_ON -> [LLMProviderService]
@@ -56,6 +57,7 @@ def _is_valid_runtime_api_key(value: str | None) -> bool:
# #region get_providers [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve all LLM provider configurations.
# @PRE User is authenticated.
# @POST Returns list of LLMProviderConfig.
@@ -96,6 +98,7 @@ async def get_providers(
# #region fetch_models [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch available models from an LLM provider by base_url+provider_type, or by provider_id.
# @PRE User is authenticated. Either provider_id or base_url+provider_type must be provided.
# @POST Returns a list of available model IDs.
@@ -175,6 +178,7 @@ async def fetch_models(
# #region get_llm_status [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Returns whether LLM runtime is configured for dashboard validation.
# @PRE User is authenticated.
# @POST configured=true only when an active provider with valid decrypted key exists.
@@ -246,6 +250,7 @@ async def get_llm_status(
# #region create_provider [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new LLM provider configuration.
# @PRE User is authenticated and has admin permissions.
# @POST Returns the created LLMProviderConfig.
@@ -283,6 +288,7 @@ async def create_provider(
# #region update_provider [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing LLM provider configuration.
# @PRE User is authenticated and has admin permissions.
# @POST Returns the updated LLMProviderConfig.
@@ -322,6 +328,7 @@ async def update_provider(
# #region delete_provider [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete an LLM provider configuration.
# @PRE User is authenticated and has admin permissions.
# @POST Returns success status.
@@ -360,6 +367,7 @@ async def delete_provider(
# #region test_connection [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Test connection to an LLM provider.
# @PRE User is authenticated.
# @POST Returns success status and message.
@@ -415,6 +423,7 @@ async def test_connection(
# #region test_provider_config [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Test connection with a provided configuration (not yet saved).
# @PRE User is authenticated.
# @POST Returns success status and message.
@@ -466,6 +475,7 @@ class ProbeMaxImagesResponse(BaseModel):
# #region probe_max_images [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Probe an LLM provider to discover its max images per request limit.
# @PRE Provider exists and has valid API key.
# @POST Returns the detected max_images limit and caches it on the provider.

View File

@@ -1,4 +1,5 @@
# #region MaintenanceRouter [C:3] [TYPE Module] [SEMANTICS fastapi, router, maintenance, api]
# @defgroup Api Module group.
# @BRIEF FastAPI APIRouter for the Maintenance Banner endpoints.
# @LAYER API
# @RELATION DEPENDS_ON -> [MaintenanceRoutesModule]

View File

@@ -1,4 +1,5 @@
# #region MaintenanceRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, routes, maintenance, api]
# @defgroup Api Module group.
# @BRIEF Route handler stubs for all 7 Maintenance Banner API endpoints with RBAC guards per FR-015.
# @LAYER API
# @RELATION DEPENDS_ON -> [MaintenanceSchemasModule]
@@ -52,6 +53,7 @@ from ._schemas import (
# FR-015: viewer, operator, admin all have READ access
# #region list_dashboard_banners [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get per-dashboard banner state for the Dashboard Hub indicator.
# @RELATION DEPENDS_ON -> [EXT:code:has_permission_maintenance_READ]
@router.get("/dashboard-banners", response_model=list[MaintenanceDashboardBannerState])
@@ -116,6 +118,7 @@ async def list_dashboard_banners(
# FR-015: operator, admin (NOT viewer)
# #region list_events [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get active and completed maintenance event lists with affected dashboard counts.
# @RELATION DEPENDS_ON -> [EXT:code:has_permission_maintenance_READ]
@router.get("/events", response_model=MaintenanceEventListResponse)
@@ -221,6 +224,7 @@ async def list_events(
# FR-015: operator, admin (NOT viewer)
# #region start_maintenance [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Start a maintenance event. Validates input, creates event, dispatches TaskManager task.
# Always returns 202 with task_id and maintenance_id.
# Idempotency: same (tables, start_time, end_time) returns already_active (409-like but 200).
@@ -374,6 +378,7 @@ async def start_maintenance(
# FR-015: operator, admin (NOT viewer)
# #region end_maintenance [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF End a specific maintenance event by ID. Removes banners from affected dashboards.
# Always returns 202 with task_id. Idempotent: already_completed returns success.
# @RELATION DEPENDS_ON -> [EXT:code:has_permission("maintenance", "WRITE")]
@@ -450,6 +455,7 @@ async def end_maintenance(
# FR-015: operator, admin (NOT viewer)
# #region end_all_maintenance [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF End all active maintenance events. Removes all banners from all dashboards.
# Always returns 202 with task_id. Supports environment scoping for API keys.
# @RELATION DEPENDS_ON -> [EXT:code:has_permission("maintenance", "WRITE")]
@@ -520,6 +526,7 @@ async def end_all_maintenance(
# FR-015: operator, admin (NOT viewer)
# #region get_maintenance_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get current maintenance settings configuration.
# @RELATION DEPENDS_ON -> [EXT:code:has_permission_maintenance_READ]
@router.get("/settings", response_model=MaintenanceSettingsResponse)
@@ -555,6 +562,7 @@ async def get_maintenance_settings(
# FR-015: admin ONLY (NOT operator, NOT viewer)
# #region update_maintenance_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Update maintenance settings. All fields optional for partial update.
# admin role required per FR-015.
# @RELATION DEPENDS_ON -> [EXT:code:has_permission("admin:settings", "WRITE")]

View File

@@ -1,4 +1,5 @@
# #region MaintenanceSchemasModule [C:2] [TYPE Module] [SEMANTICS pydantic, schema, maintenance, request, response]
# @defgroup Api Module group.
# @BRIEF Pydantic models for Maintenance Banner API: request/response schemas per data-model.md.
# @LAYER API
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]

View File

@@ -1,4 +1,5 @@
# #region MappingsApi [C:3] [TYPE Module] [SEMANTICS fastapi, mapping, api, mapping-create]
# @defgroup Api Module group.
#
# @BRIEF API endpoints for managing database mappings and getting suggestions.
# @LAYER API
@@ -21,6 +22,7 @@ from ...models.mapping import DatabaseMapping
router = APIRouter(tags=["mappings"])
# #region MappingCreate [TYPE DataClass]
# @ingroup Api
class MappingCreate(BaseModel):
source_env_id: str
target_env_id: str
@@ -32,6 +34,7 @@ class MappingCreate(BaseModel):
# #endregion MappingCreate
# #region MappingResponse [TYPE DataClass]
# @ingroup Api
class MappingResponse(BaseModel):
id: str
source_env_id: str
@@ -46,12 +49,14 @@ class MappingResponse(BaseModel):
# #endregion MappingResponse
# #region SuggestRequest [TYPE DataClass]
# @ingroup Api
class SuggestRequest(BaseModel):
source_env_id: str
target_env_id: str
# #endregion SuggestRequest
# #region get_mappings [TYPE Function]
# @ingroup Api
# @BRIEF List all saved database mappings.
# @PRE db session is injected.
# @POST Returns filtered list of DatabaseMapping records.
@@ -72,6 +77,7 @@ async def get_mappings(
# #endregion get_mappings
# #region create_mapping [TYPE Function]
# @ingroup Api
# @BRIEF Create or update a database mapping.
# @PRE mapping is valid MappingCreate, db session is injected.
# @POST DatabaseMapping created or updated in database.
@@ -105,6 +111,7 @@ async def create_mapping(
# #endregion create_mapping
# #region suggest_mappings_api [TYPE Function]
# @ingroup Api
# @BRIEF Get suggested mappings based on fuzzy matching.
# @PRE request is valid SuggestRequest, config_manager is injected.
# @POST Returns mapping suggestions.

View File

@@ -1,4 +1,5 @@
# #region MigrationApi [C:5] [TYPE Module] [SEMANTICS fastapi, migration, api, sync, search, mapping]
# @defgroup Api Module group.
# @BRIEF HTTP contract layer for migration orchestration, settings, dry-run, and mapping sync endpoints.
# @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [AppDependencies]
@@ -41,6 +42,7 @@ router = APIRouter(prefix="/api", tags=["migration"])
# #region get_dashboards [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch dashboard metadata from a requested environment for migration selection UI.
# @PRE env_id is provided and exists in configured environments.
# @POST Returns List[DashboardMetadata] for the resolved environment; emits HTTP_404 when environment is absent.
@@ -72,6 +74,7 @@ async def get_dashboards(
# #region execute_migration [C:5] [TYPE Function]
# @ingroup Api
# @BRIEF Validate migration selection and enqueue asynchronous migration task execution.
# @PRE DashboardSelection payload is valid and both source/target environments exist.
# @POST Returns {"task_id": str, "message": str} when task creation succeeds; emits HTTP_400/HTTP_500 on failure.
@@ -135,6 +138,7 @@ async def execute_migration(
# #region dry_run_migration [C:5] [TYPE Function]
# @ingroup Api
# @BRIEF Build pre-flight migration diff and risk summary without mutating target systems.
# @PRE DashboardSelection is valid, source and target environments exist, differ, and selected_ids is non-empty.
# @POST Returns deterministic dry-run payload; emits HTTP_400 for guard violations and HTTP_500 for orchestrator value errors.
@@ -201,6 +205,7 @@ async def dry_run_migration(
# #region get_migration_settings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Read and return configured migration synchronization cron expression.
# @PRE Configuration store is available and requester has READ permission.
# @POST Returns {"cron": str} reflecting current persisted settings value.
@@ -222,6 +227,7 @@ async def get_migration_settings(
# #region update_migration_settings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Validate and persist migration synchronization cron expression update.
# @PRE Payload includes "cron" key and requester has WRITE permission.
# @POST Returns {"cron": str, "status": "updated"} and persists updated cron value.
@@ -253,6 +259,7 @@ async def update_migration_settings(
# #region get_resource_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch synchronized resource mappings with optional filters and pagination for migration mappings view.
# @PRE skip>=0, 1<=limit<=500, DB session is active, requester has READ permission.
# @POST Returns {"items": [...], "total": int} where items reflect applied filters and pagination.
@@ -325,6 +332,7 @@ async def get_resource_mappings(
# #region trigger_sync_now [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger immediate ID synchronization for every configured environment.
# @PRE At least one environment is configured and requester has EXECUTE permission.
# @POST Returns sync summary with synced/failed counts after attempting all environments.

View File

@@ -1,4 +1,5 @@
# #region PluginsRouter [C:3] [TYPE Module] [SEMANTICS fastapi, plugin, api]
# @defgroup Api Module group.
# @BRIEF Defines the FastAPI router for plugin-related endpoints, allowing clients to list available plugins.
# @LAYER API
# @RELATION DEPENDS_ON -> [PluginConfig]
@@ -15,6 +16,7 @@ router = APIRouter()
# #region list_plugins [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a list of all available plugins.
# @PRE plugin_loader is injected via Depends.
# @POST Returns a list of PluginConfig objects.

View File

@@ -1,4 +1,5 @@
# #region ProfileApiModule [C:5] [TYPE Module] [SEMANTICS fastapi, profile, api, validate, search, superset]
# @defgroup Api Module group.
#
# @BRIEF Exposes self-scoped profile preference endpoints and environment-based Superset account lookup.
# @LAYER API
@@ -60,6 +61,7 @@ def _get_profile_service(db: Session, config_manager, plugin_loader=None) -> Pro
# #region get_preferences [TYPE Function]
# @ingroup Api
# @RELATION CALLS -> ProfileService
# @BRIEF Get authenticated user's dashboard filter preference.
# @PRE Valid JWT and authenticated user context.
@@ -79,6 +81,7 @@ async def get_preferences(
# #region update_preferences [TYPE Function]
# @ingroup Api
# @RELATION CALLS -> ProfileService
# @BRIEF Update authenticated user's dashboard filter preference.
# @PRE Valid JWT and valid request payload.
@@ -106,6 +109,7 @@ async def update_preferences(
# #region lookup_superset_accounts [TYPE Function]
# @ingroup Api
# @RELATION CALLS -> ProfileService
# @BRIEF Lookup Superset account candidates in selected environment.
# @PRE Valid JWT, authenticated context, and environment_id query parameter.

View File

@@ -1,4 +1,5 @@
# #region ReportsRouter [C:5] [TYPE Module] [SEMANTICS fastapi, report, api, transform, search, task]
# @defgroup Api Module group.
# @BRIEF FastAPI router for unified task report list and detail retrieval endpoints.
# @LAYER API
# @RELATION DEPENDS_ON -> [ReportsService]
@@ -69,6 +70,7 @@ def _parse_csv_enum_list(raw: str | None, enum_cls, field_name: str) -> list:
# #region list_reports [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return paginated unified reports list.
# @PRE authenticated/authorized request and validated query params.
# @POST returns {items,total,page,page_size,has_next,applied_filters}.
@@ -144,6 +146,7 @@ async def list_reports(
# #region get_report_detail [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return one normalized report detail with diagnostics and next actions.
# @PRE authenticated/authorized request and existing report_id.
# @POST returns normalized detail envelope or 404 when report is not found.

View File

@@ -1,4 +1,5 @@
# #region SettingsRouter [C:5] [TYPE Module] [SEMANTICS fastapi, api, superset, logging-config-response]
# @defgroup Api Module group.
#
# @BRIEF Provides API endpoints for managing application settings and Superset environments.
# @LAYER API
@@ -91,6 +92,7 @@ async def _validate_superset_connection_fast(env: Environment) -> None:
# #region get_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves all application settings.
# @PRE Config manager is available.
# @POST Returns masked AppConfig.
@@ -145,6 +147,7 @@ async def get_allowed_languages(
# #region update_global_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates global application settings.
# @PRE New settings are provided.
# @POST Global settings are updated.
@@ -165,6 +168,7 @@ async def update_global_settings(
# #region get_storage_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves storage-specific settings.
@router.get("/storage", response_model=StorageConfig)
async def get_storage_settings(
@@ -179,6 +183,7 @@ async def get_storage_settings(
# #region update_storage_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates storage-specific settings.
# @POST Storage settings are updated and saved.
@router.put("/storage", response_model=StorageConfig)
@@ -202,6 +207,7 @@ async def update_storage_settings(
# #region get_environments [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all configured Superset environments.
# @PRE Config manager is available.
# @POST Returns list of environments.
@@ -223,6 +229,7 @@ async def get_environments(
# #region add_environment [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Adds a new Superset environment.
# @PRE Environment data is valid and reachable.
# @POST Environment is added to config.
@@ -255,6 +262,7 @@ async def add_environment(
# #region update_environment [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing Superset environment.
# @PRE ID and valid environment data are provided.
# @POST Environment is updated in config.
@@ -298,6 +306,7 @@ async def update_environment(
# #region delete_environment [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Deletes a Superset environment.
# @PRE ID is provided.
# @POST Environment is removed from config.
@@ -315,6 +324,7 @@ async def delete_environment(
# #region test_environment_connection [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Tests the connection to a Superset environment.
# @PRE ID is provided.
# @POST Returns success or error status.
@@ -348,6 +358,7 @@ async def test_environment_connection(
# #region get_logging_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves current logging configuration.
# @PRE Config manager is available.
# @POST Returns logging configuration.
@@ -369,6 +380,7 @@ async def get_logging_config(
# #region update_logging_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates logging configuration.
# @PRE New logging config is provided.
# @POST Logging configuration is updated and saved.
@@ -417,6 +429,7 @@ class ConsolidatedSettingsResponse(BaseModel):
# #region get_consolidated_settings [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves all settings categories in a single call.
# @PRE Config manager is available and the caller holds admin settings read permission.
# @POST Returns consolidated settings, provider metadata, and persisted notification payload in one stable response.
@@ -500,6 +513,7 @@ async def get_consolidated_settings(
# #region update_consolidated_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Bulk update application settings from the consolidated view.
# @PRE User has admin permissions, config is valid.
# @POST Settings are updated and saved via ConfigManager.
@@ -569,6 +583,7 @@ async def update_consolidated_settings(
# #region get_validation_policies [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all validation policies.
@router.get("/automation/policies", response_model=list[ValidationPolicyResponse])
async def get_validation_policies(
@@ -582,6 +597,7 @@ async def get_validation_policies(
# #region create_validation_policy [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Creates a new validation policy.
@router.post("/automation/policies", response_model=ValidationPolicyResponse)
async def create_validation_policy(
@@ -601,6 +617,7 @@ async def create_validation_policy(
# #region update_validation_policy [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing validation policy.
@router.patch("/automation/policies/{id}", response_model=ValidationPolicyResponse)
async def update_validation_policy(
@@ -627,6 +644,7 @@ async def update_validation_policy(
# #region delete_validation_policy [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Deletes a validation policy.
@router.delete("/automation/policies/{id}")
async def delete_validation_policy(
@@ -648,6 +666,7 @@ async def delete_validation_policy(
# #region get_translation_schedules [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all translation schedules joined with translation job names.
@router.get("/automation/translation-schedules", response_model=list[TranslationScheduleItem])
async def get_translation_schedules(

View File

@@ -1,4 +1,5 @@
# #region storage_routes [C:5] [TYPE Module] [SEMANTICS fastapi, storage, api, upload, download]
# @defgroup Api Module group.
#
# @BRIEF API endpoints for file storage management (backups and repositories).
# @LAYER API
@@ -20,6 +21,7 @@ from ...plugins.storage.plugin import StoragePlugin
router = APIRouter(tags=["storage"])
# #region list_files [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List all files and directories in the storage system.
#
# @PRE None.
@@ -42,6 +44,7 @@ async def list_files(
# #endregion list_files
# #region upload_file [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Upload a file to the storage system.
#
# @PRE category must be a valid FileCategory.
@@ -71,6 +74,7 @@ async def upload_file(
# #endregion upload_file
# #region delete_file [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a specific file or directory.
#
# @PRE category must be a valid FileCategory.
@@ -100,6 +104,7 @@ async def delete_file(
# #endregion delete_file
# #region download_file [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a file for download.
#
# @PRE category must be a valid FileCategory.
@@ -129,6 +134,7 @@ async def download_file(
# #endregion download_file
# #region get_file_by_path [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a file by validated absolute/relative path under storage root.
#
# @PRE path must resolve under configured storage root.

View File

@@ -1,4 +1,5 @@
# #region TasksRouter [C:3] [TYPE Module] [SEMANTICS fastapi, task, api, search, create-task-request, resolve-task-request]
# @defgroup Api Module group.
# @BRIEF Defines the FastAPI router for task-related endpoints, allowing clients to create, list, and get the status of tasks.
# @LAYER API
# @RELATION DEPENDS_ON -> [TaskManager]
@@ -48,6 +49,7 @@ class ResumeTaskRequest(BaseModel):
# #region create_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Create and start a new task for a given plugin.
# @PRE plugin_id must exist and params must be valid for that plugin.
# @POST A new task is created and started.
@@ -132,6 +134,7 @@ async def create_task(
# #region list_tasks [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a list of tasks with pagination and optional status filter.
# @PRE task_manager must be available.
# @POST Returns a list of tasks.
@@ -177,6 +180,7 @@ async def list_tasks(
# #region get_task [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve the details of a specific task.
# @PRE task_id must exist.
# @POST Returns task details or raises 404.
@@ -200,6 +204,7 @@ async def get_task(
# #region get_task_logs [C:5] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve logs for a specific task with optional filtering.
# @PRE task_id must exist.
# @POST Returns a list of log entries or raises 404.
@@ -247,6 +252,7 @@ async def get_task_logs(
# #region get_task_log_stats [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get statistics about logs for a task (counts by level and source).
# @PRE task_id must exist.
# @POST Returns log statistics or raises 404.
@@ -292,6 +298,7 @@ async def get_task_log_stats(
# #region get_task_log_sources [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get unique sources for a task's logs.
# @PRE task_id must exist.
# @POST Returns list of unique source names or raises 404.
@@ -314,6 +321,7 @@ async def get_task_log_sources(
# #region resolve_task [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Resolve a task that is awaiting mapping.
# @PRE task must be in AWAITING_MAPPING status.
# @POST Task is resolved and resumes execution.
@@ -337,6 +345,7 @@ async def resolve_task(
# #region resume_task [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Resume a task that is awaiting input (e.g., passwords).
# @PRE task must be in AWAITING_INPUT status.
# @POST Task resumes execution with provided input.
@@ -360,6 +369,7 @@ async def resume_task(
# #region clear_tasks [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Clear tasks matching the status filter.
# @PRE task_manager is available.
# @POST Tasks are removed from memory/persistence.

View File

@@ -1,4 +1,5 @@
# #region TranslateRoutes [C:4] [TYPE Module] [SEMANTICS translate, api, package, schedule, dashboard, llm]
# @defgroup Api Module group.
# @BRIEF API routes for LLM-based SQL/dashboard translation management including terminology dictionary CRUD and import.
# @LAYER API
# @RELATION DEPENDS_ON -> [SupersetClient]

View File

@@ -1,4 +1,5 @@
# #region TranslateCorrectionRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]
# @defgroup Api Module group.
# @BRIEF Term correction submission endpoints.
# @LAYER API
@@ -21,6 +22,7 @@ from ._router import router
# ============================================================
# #region submit_correction [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Submit a term correction from a run result review.
# @PRE User has translate.dictionary.edit permission.
# @POST Correction applied with conflict detection.
@@ -58,6 +60,7 @@ async def submit_correction(
# #region submit_bulk_corrections [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Submit multiple term corrections atomically.
# @PRE User has translate.dictionary.edit permission.
# @POST All corrections applied or none with conflict list.

View File

@@ -1,4 +1,5 @@
# #region TranslateDictionaryRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]
# @defgroup Api Module group.
# @BRIEF Terminology Dictionary CRUD, entries management, and import routes.
# @LAYER API
@@ -23,6 +24,7 @@ from ._router import router
# ============================================================
# #region list_dictionaries [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List all terminology dictionaries.
# @PRE User has translate.dictionary.view permission.
# @POST Returns list of dictionaries with total count.
@@ -47,6 +49,7 @@ async def list_dictionaries(
# #region get_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get a single terminology dictionary by ID.
# @PRE User has translate.dictionary.view permission.
# @POST Returns the dictionary with entry_count.
@@ -72,6 +75,7 @@ async def get_dictionary(
# #region create_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new terminology dictionary.
# @PRE User has translate.dictionary.create permission.
# @POST Returns the created dictionary.
@@ -98,6 +102,7 @@ async def create_dictionary(
# #region update_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing terminology dictionary.
# @PRE User has translate.dictionary.edit permission.
# @POST Returns the updated dictionary.
@@ -130,6 +135,7 @@ async def update_dictionary(
# #region delete_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs.
# @PRE User has translate.dictionary.delete permission.
# @POST Dictionary is deleted.
@@ -159,6 +165,7 @@ async def delete_dictionary(
# ============================================================
# #region list_dictionary_entries [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List entries for a dictionary, optionally filtered by language pair.
# @PRE User has translate.dictionary.view permission.
# @POST Returns paginated list of entries with language pair fields.
@@ -218,6 +225,7 @@ async def list_dictionary_entries(
# #region add_dictionary_entry [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Add a new entry to a dictionary.
# @PRE User has translate.dictionary.edit permission.
# @POST Entry is created.
@@ -269,6 +277,7 @@ async def add_dictionary_entry(
# #region edit_dictionary_entry [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing dictionary entry.
# @PRE User has translate.dictionary.edit permission.
# @POST Entry is updated.
@@ -321,6 +330,7 @@ async def edit_dictionary_entry(
# #region delete_dictionary_entry [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a dictionary entry.
# @PRE User has translate.dictionary.edit permission.
# @POST Entry is deleted.
@@ -345,6 +355,7 @@ async def delete_dictionary_entry(
# #region import_dictionary_entries [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Import entries into a terminology dictionary from CSV/TSV content.
# @PRE User has translate.dictionary.edit permission.
# @POST Entries are imported per conflict mode.

View File

@@ -1,4 +1,5 @@
# #region TranslateHelpersModule [C:2] [TYPE Module] [SEMANTICS sqlalchemy, translate, helper, query, mapping]
# @defgroup Api Module group.
# @BRIEF Shared helper functions for translate route handlers.
# @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:models.translate]

View File

@@ -1,4 +1,5 @@
# #region TranslateJobRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]
# @defgroup Api Module group.
# @BRIEF Translation Job CRUD and datasource column routes.
# @LAYER API
@@ -33,6 +34,7 @@ from ._router import router
# ============================================================
# #region list_jobs [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List all translation jobs.
# @PRE User has translate.job.view permission.
# @POST Returns list of translation jobs.
@@ -66,6 +68,7 @@ async def list_jobs(
# #region get_job [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get a single translation job by ID.
# @PRE User has translate.job.view permission.
# @POST Returns the translation job.
@@ -90,6 +93,7 @@ async def get_job(
# #region create_job [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new translation job.
# @PRE User has translate.job.create permission.
# @POST Returns the created translation job.
@@ -115,6 +119,7 @@ async def create_job(
# #region update_job [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing translation job.
# @PRE User has translate.job.edit permission.
# @POST Returns the updated translation job.
@@ -141,6 +146,7 @@ async def update_job(
# #region delete_job [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a translation job.
# @PRE User has translate.job.delete permission.
# @POST Job is deleted.
@@ -163,6 +169,7 @@ async def delete_job(
# #region duplicate_job [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Duplicate a translation job.
# @PRE User has translate.job.create permission.
# @POST Returns the duplicated job with status DRAFT.
@@ -190,6 +197,7 @@ async def duplicate_job(
# #region get_datasource_columns [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get column metadata and database dialect for a Superset datasource.
# @PRE User has translate.job.view permission.
# @POST Returns column list with metadata and database_dialect.
@@ -249,6 +257,7 @@ async def get_datasource_columns(
# #region check_target_schema [C:3] [TYPE Function] [SEMANTICS api,translate,validate]
# @ingroup Api
# @BRIEF Проверяет схему целевой таблицы: какие колонки ожидаются, какие есть, каких не хватает.
# @RELATION DEPENDS_ON -> [validate_target_table_schema]
# @RELATION DEPENDS_ON -> [TargetSchemaValidationRequest]

View File

@@ -1,4 +1,5 @@
# #region TranslateMetricsRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]
# @defgroup Api Module group.
# @BRIEF Translation Metrics endpoints.
# @LAYER API
@@ -18,6 +19,7 @@ from ._router import router
# ============================================================
# #region get_metrics [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get translation metrics, optionally filtered by job.
# @PRE User has translate.metrics.view permission.
# @POST Returns metrics data.
@@ -41,6 +43,7 @@ async def get_metrics(
# #region get_job_metrics [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get aggregated metrics for a specific job.
# @PRE User has translate.metrics.view permission.
# @POST Returns metrics dict.

View File

@@ -1,4 +1,5 @@
# #region TranslatePreviewRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, preview, review, api, search]
# @defgroup Api Module group.
# @BRIEF Translation Preview session management routes.
# @LAYER API
@@ -24,6 +25,7 @@ from ._router import router
# ============================================================
# #region preview_translation [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Preview a translation before applying it.
# @PRE User has translate.job.execute permission.
# @POST Returns a preview session with records and cost estimation.
@@ -60,6 +62,7 @@ async def preview_translation(
# #region update_preview_row [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Approve, edit, or reject a preview row (optionally per language).
# @PRE User has translate.job.execute permission.
# @POST Preview row status is updated.
@@ -92,6 +95,7 @@ async def update_preview_row(
# #region accept_preview_session [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Accept a preview session, marking it as the quality gate for full execution.
# @PRE User has translate.job.execute permission. Job has an ACTIVE preview session.
# @POST Preview session is marked as APPLIED; full execution can proceed.
@@ -115,6 +119,7 @@ async def accept_preview_session(
# #region apply_preview [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Apply a preview session (alias for accept when accepting at session level).
# @PRE User has translate.job.execute permission.
# @POST Preview is applied.
@@ -147,6 +152,7 @@ async def apply_preview(
# ============================================================
# #region get_preview_records [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get records for a preview session.
# @PRE User has translate.job.view permission.
# @POST Returns list of preview records.

View File

@@ -5,6 +5,7 @@
from fastapi import APIRouter
# #region translate_router [TYPE Global]
# @ingroup Api
# @BRIEF APIRouter instance for all translate sub-routes.
router = APIRouter(prefix="/api/translate", tags=["translate"])

View File

@@ -1,4 +1,5 @@
# #region TranslateRunEditRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, edit, correction, bulk, override]
# @defgroup Api Module group.
# @BRIEF Translation Run inline edit, bulk find-replace, and language override routes.
# @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunRoutesModule]
@@ -21,6 +22,7 @@ from ._router import router
# #region override_detected_language [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Manually override the detected source language for a specific translation language entry.
# @PRE User has translate.job.execute permission. Run, record, and language entry exist.
# @POST TranslationLanguage.source_language_detected is updated; language_overridden is set to True.
@@ -96,6 +98,7 @@ def override_detected_language(
# #region inline_edit_translation [C:4] [TYPE Function] [SEMANTICS api,translate,correction]
# @ingroup Api
# @BRIEF Apply an inline correction to a translated value on a completed run result.
# @PRE User has translate.job.execute permission. Run, record, and language entry exist.
# @POST TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission.
@@ -140,6 +143,7 @@ def inline_edit_translation(
# #region bulk_find_replace [C:4] [TYPE Function] [SEMANTICS api,translate,bulk,replace]
# @ingroup Api
# @BRIEF Perform bulk find-and-replace on translated values within a run.
# @PRE User has translate.job.execute permission. Run exists.
# @POST If preview=false, matching translations are updated. Optional dictionary submission.

View File

@@ -1,4 +1,5 @@
# #region TranslateRunHistoryRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, history, status, records]
# @defgroup Api Module group.
# @BRIEF Translation Run history, status, records and batches routes.
# @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunRoutesModule]
@@ -20,6 +21,7 @@ from datetime import datetime, UTC
# #region get_run_history [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get run history for a translation job.
# @PRE User has translate.history.view permission.
# @POST Returns list of runs.
@@ -44,6 +46,7 @@ def get_run_history(
# #region get_run_status [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get status and statistics for a translation run.
# @PRE User has translate.history.view permission.
# @POST Returns run details with statistics.
@@ -65,6 +68,7 @@ def get_run_status(
# #region get_run_records [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get paginated records for a translation run.
# @PRE User has translate.history.view permission.
# @POST Returns paginated records.
@@ -90,6 +94,7 @@ def get_run_records(
# #region get_batches [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get batches for a translation run.
# @PRE User has translate.job.view permission.
# @POST Returns list of batches.

View File

@@ -1,4 +1,5 @@
# #region TranslateRunListRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, download, search]
# @defgroup Api Module group.
# @BRIEF Translation Run listing, detail, and CSV download routes (cross-job).
# @LAYER API
@@ -20,6 +21,7 @@ from ._router import router
# ============================================================
# #region list_runs [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List all runs with cross-job filtering and pagination.
# @PRE User has translate.history.view permission.
# @POST Returns paginated list of runs.
@@ -128,6 +130,7 @@ async def list_runs(
# ============================================================
# #region get_run_detail [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get detailed run info with config_snapshot, records, events.
# @PRE User has translate.history.view permission.
# @POST Returns run detail with records and events.
@@ -171,6 +174,7 @@ async def get_run_detail(
# ============================================================
# #region download_skipped_csv [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Download a CSV of skipped translation records from a run.
# @PRE User has translate.job.view permission.
# @POST Returns CSV file of skipped records.
@@ -229,6 +233,7 @@ async def download_skipped_csv(
# ============================================================
# #region download_failed_csv [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Download a CSV of successfully translated but uninserted records from a FAILED run.
# @PRE User has translate.job.view permission. Run must have insert_status=failed or status=FAILED.
# @POST Returns CSV file of records that were translated but not inserted into target DB.

View File

@@ -1,4 +1,5 @@
# #region TranslateRunRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, execution]
# @defgroup Api Module group.
# @BRIEF Translation Run execution, retry, and cancel routes.
# @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunHistoryRoutesModule]
@@ -23,6 +24,7 @@ from ._router import router
# #region run_translation [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Execute a translation job (trigger a run).
# @PRE User has translate.job.execute permission.
# @POST Returns the created translation run.
@@ -128,6 +130,7 @@ async def run_translation(
# #region retry_run [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retry failed batches in a translation run.
# @PRE User has translate.job.execute permission.
# @POST Returns the updated translation run.
@@ -154,6 +157,7 @@ async def retry_run(
# #region retry_insert [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retry the SQL insert phase for a completed run.
# @PRE User has translate.job.execute permission.
# @POST Returns the updated run.
@@ -180,6 +184,7 @@ async def retry_insert(
# #region cancel_run [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Cancel a running translation.
# @PRE User has translate.job.execute permission.
# @POST Run is cancelled.

View File

@@ -1,4 +1,5 @@
# #region TranslateScheduleRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, schedule, search]
# @defgroup Api Module group.
# @BRIEF Translation Schedule management routes.
# @LAYER API
@@ -19,6 +20,7 @@ from ._router import router
# ============================================================
# #region get_schedule [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get the schedule for a translation job.
# @PRE User has translate.schedule.view permission.
# @POST Returns the schedule configuration.
@@ -54,6 +56,7 @@ async def get_schedule(
# #region set_schedule [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Set or update the schedule for a translation job.
# @PRE User has translate.schedule.manage permission.
# @POST Schedule is created or updated.
@@ -120,6 +123,7 @@ async def set_schedule(
# #region enable_schedule [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Enable a schedule for a translation job.
# @PRE User has translate.schedule.manage permission.
# @POST Schedule is enabled.
@@ -152,6 +156,7 @@ async def enable_schedule(
# #region disable_schedule [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Disable a schedule for a translation job.
# @PRE User has translate.schedule.manage permission.
# @POST Schedule is disabled.
@@ -178,6 +183,7 @@ async def disable_schedule(
# #region delete_schedule [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete the schedule for a translation job.
# @PRE User has translate.schedule.manage permission.
# @POST Schedule is removed.
@@ -204,6 +210,7 @@ async def delete_schedule(
# #region get_next_executions [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Preview next N executions for a job's schedule.
# @PRE User has translate.schedule.view permission.
# @POST Returns next execution times.

View File

@@ -35,18 +35,19 @@ from unittest.mock import AsyncMock, MagicMock
from fastapi.testclient import TestClient
from src.api.routes.validation import _get_run_service, _get_task_service
from src.api.routes.validation_tasks import _get_task_service
from src.app import app
from src.dependencies import (
get_config_manager,
get_current_user,
get_scheduler_service,
get_task_manager,
)
from src.models.auth import User
from src.schemas.validation import (
TriggerRunResponse,
ValidationRunDetailResponse,
ValidationRunListResponse,
RunDetailResponse,
RunListResponse,
ValidationTaskListResponse,
ValidationTaskResponse,
)
@@ -78,19 +79,20 @@ def mock_all_deps():
# Reset user state — Admin role passes all permission gates
mock_user.roles = [admin_role]
mock_task_service = MagicMock()
mock_run_service = MagicMock()
mock_validation_service = MagicMock()
mock_task_manager = MagicMock()
app.dependency_overrides[_get_task_service] = lambda: mock_task_service
app.dependency_overrides[_get_run_service] = lambda: mock_run_service
mock_scheduler_service = MagicMock()
app.dependency_overrides[_get_task_service] = lambda: mock_validation_service
app.dependency_overrides[get_config_manager] = lambda: MagicMock()
app.dependency_overrides[get_task_manager] = lambda: mock_task_manager
app.dependency_overrides[get_scheduler_service] = lambda: mock_scheduler_service
app.dependency_overrides[get_current_user] = lambda: mock_user
yield {
"task_service": mock_task_service,
"run_service": mock_run_service,
"task_service": mock_validation_service,
"run_service": mock_validation_service,
"task_manager": mock_task_manager,
}
app.dependency_overrides.clear()
@@ -146,7 +148,7 @@ def test_list_tasks_success(mock_all_deps):
task = _make_task_response()
mock_all_deps["task_service"].list_tasks.return_value = (1, [task])
response = client.get("/api/validation/tasks")
response = client.get("/api/validation-tasks")
assert response.status_code == 200
data = response.json()
assert data["items"][0]["name"] == "Test Task"
@@ -156,7 +158,7 @@ def test_list_tasks_success(mock_all_deps):
ValidationTaskListResponse(**data)
# environment_id filter
response = client.get("/api/validation/tasks?environment_id=env-1")
response = client.get("/api/validation-tasks?environment_id=env-1")
assert response.status_code == 200
mock_all_deps["task_service"].list_tasks.assert_called_with(
is_active=None, environment_id="env-1", page=1, page_size=20
@@ -167,13 +169,13 @@ def test_list_tasks_success(mock_all_deps):
# #region test_list_tasks_pagination [C:2] [TYPE Function]
# @BRIEF Pagination query params are validated: page >= 1, page_size 1-100.
def test_list_tasks_pagination(mock_all_deps):
response = client.get("/api/validation/tasks?page=0")
response = client.get("/api/validation-tasks?page=0")
assert response.status_code == 422
response = client.get("/api/validation/tasks?page_size=101")
response = client.get("/api/validation-tasks?page_size=101")
assert response.status_code == 422
response = client.get("/api/validation/tasks?page_size=0")
response = client.get("/api/validation-tasks?page_size=0")
assert response.status_code == 422
# #endregion test_list_tasks_pagination
@@ -182,7 +184,7 @@ def test_list_tasks_pagination(mock_all_deps):
# @BRIEF Service ValueError becomes 400 on list_tasks.
def test_list_tasks_service_error(mock_all_deps):
mock_all_deps["task_service"].list_tasks.side_effect = ValueError("Bad filter")
response = client.get("/api/validation/tasks?environment_id=nonexistent")
response = client.get("/api/validation-tasks?environment_id=nonexistent")
assert response.status_code == 400
assert "Bad filter" in response.json()["detail"]
# #endregion test_list_tasks_service_error
@@ -201,7 +203,7 @@ def test_create_task_success(mock_all_deps):
"notify_owners": True,
"alert_condition": "FAIL_ONLY",
}
response = client.post("/api/validation/tasks", json=payload)
response = client.post("/api/validation-tasks", json=payload)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Test Task"
@@ -215,19 +217,19 @@ def test_create_task_success(mock_all_deps):
# @BRIEF POST with missing required fields returns 422.
def test_create_task_missing_fields(mock_all_deps):
# Empty payload
response = client.post("/api/validation/tasks", json={})
response = client.post("/api/validation-tasks", json={})
assert response.status_code == 422
errors = response.json()
assert "detail" in errors
# Missing provider_id
response = client.post("/api/validation/tasks", json={
response = client.post("/api/validation-tasks", json={
"name": "Test", "environment_id": "env-1", "dashboard_ids": ["dash-1"],
})
assert response.status_code == 422
# Missing dashboard_ids
response = client.post("/api/validation/tasks", json={
response = client.post("/api/validation-tasks", json={
"name": "Test", "environment_id": "env-1", "provider_id": "provider-1",
})
assert response.status_code == 422
@@ -244,7 +246,7 @@ def test_create_task_invalid_provider(mock_all_deps):
"name": "Test", "environment_id": "env-1",
"dashboard_ids": ["dash-1"], "provider_id": "provider-2",
}
response = client.post("/api/validation/tasks", json=payload)
response = client.post("/api/validation-tasks", json=payload)
assert response.status_code == 422
assert "not multimodal" in response.json()["detail"]
# #endregion test_create_task_invalid_provider
@@ -256,7 +258,7 @@ def test_get_task_success(mock_all_deps):
mock_all_deps["task_service"].get_task.return_value = _make_task_response()
mock_all_deps["run_service"].list_runs.return_value = (0, [])
response = client.get("/api/validation/tasks/task-1")
response = client.get("/api/validation-tasks/task-1")
assert response.status_code == 200
data = response.json()
assert "task" in data
@@ -270,7 +272,7 @@ def test_get_task_success(mock_all_deps):
# @BRIEF GET for nonexistent task returns 404.
def test_get_task_not_found(mock_all_deps):
mock_all_deps["task_service"].get_task.side_effect = ValueError("not found")
response = client.get("/api/validation/tasks/nonexistent")
response = client.get("/api/validation-tasks/nonexistent")
assert response.status_code == 404
# #endregion test_get_task_not_found
@@ -282,7 +284,7 @@ def test_update_task_success(mock_all_deps):
mock_all_deps["task_service"].update_task.return_value = updated
payload = {"name": "Updated Name", "is_active": False}
response = client.put("/api/validation/tasks/task-1", json=payload)
response = client.put("/api/validation-tasks/task-1", json=payload)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Updated Name"
@@ -297,7 +299,7 @@ def test_update_task_success(mock_all_deps):
def test_update_task_not_found(mock_all_deps):
mock_all_deps["task_service"].update_task.side_effect = ValueError("not found")
payload = {"name": "Nope"}
response = client.put("/api/validation/tasks/nonexistent", json=payload)
response = client.put("/api/validation-tasks/nonexistent", json=payload)
assert response.status_code == 404
# #endregion test_update_task_not_found
@@ -306,7 +308,7 @@ def test_update_task_not_found(mock_all_deps):
# @BRIEF DELETE /validation/tasks/{id} returns 204.
def test_delete_task_success(mock_all_deps):
mock_all_deps["task_service"].delete_task.return_value = None
response = client.delete("/api/validation/tasks/task-1")
response = client.delete("/api/validation-tasks/task-1")
assert response.status_code == 204
mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=False)
# #endregion test_delete_task_success
@@ -316,7 +318,7 @@ def test_delete_task_success(mock_all_deps):
# @BRIEF DELETE for nonexistent task returns 404.
def test_delete_task_not_found(mock_all_deps):
mock_all_deps["task_service"].delete_task.side_effect = ValueError("not found")
response = client.delete("/api/validation/tasks/nonexistent")
response = client.delete("/api/validation-tasks/nonexistent")
assert response.status_code == 404
# #endregion test_delete_task_not_found
@@ -325,7 +327,7 @@ def test_delete_task_not_found(mock_all_deps):
# @BRIEF DELETE with delete_runs=true passes query param to service.
def test_delete_task_with_runs_flag(mock_all_deps):
mock_all_deps["task_service"].delete_task.return_value = None
response = client.delete("/api/validation/tasks/task-1?delete_runs=true")
response = client.delete("/api/validation-tasks/task-1?delete_runs=true")
assert response.status_code == 204
mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=True)
# #endregion test_delete_task_with_runs_flag
@@ -337,7 +339,7 @@ def test_trigger_run_success(mock_all_deps):
mock_all_deps["task_service"].trigger_run = AsyncMock(return_value="spawned-1")
mock_all_deps["task_manager"].create_task = AsyncMock()
response = client.post("/api/validation/tasks/task-1/run")
response = client.post("/api/validation-tasks/task-1/run")
assert response.status_code == 200
data = response.json()
assert data["task_id"] == "task-1"
@@ -353,7 +355,7 @@ def test_trigger_run_not_found(mock_all_deps):
mock_all_deps["task_service"].trigger_run = AsyncMock(
side_effect=ValueError("Validation task 'nonexistent' not found")
)
response = client.post("/api/validation/tasks/nonexistent/run")
response = client.post("/api/validation-tasks/nonexistent/run")
assert response.status_code == 422
assert "not found" in response.json()["detail"]
# #endregion test_trigger_run_not_found
@@ -365,7 +367,7 @@ def test_trigger_run_no_dashboards(mock_all_deps):
mock_all_deps["task_service"].trigger_run = AsyncMock(
side_effect=ValueError("has no dashboard IDs configured")
)
response = client.post("/api/validation/tasks/task-1/run")
response = client.post("/api/validation-tasks/task-1/run")
assert response.status_code == 422
assert "no dashboard IDs" in response.json()["detail"]
# #endregion test_trigger_run_no_dashboards
@@ -379,12 +381,12 @@ def test_list_runs_success(mock_all_deps):
run = _make_run_response()
mock_all_deps["run_service"].list_runs.return_value = (1, [run])
response = client.get("/api/validation/runs")
response = client.get("/api/validation-runs")
assert response.status_code == 200
data = response.json()
assert data["items"][0]["id"] == "run-1"
assert data["total"] == 1
ValidationRunListResponse(**data)
RunListResponse(**data)
# #endregion test_list_runs_success
@@ -402,7 +404,7 @@ def test_list_runs_filters(mock_all_deps):
"?date_to=2026-12-31",
"?status=PASS&dashboard_id=dash-1&date_from=2026-01-01",
]:
response = client.get(f"/api/validation/runs{query}")
response = client.get(f"/api/validation-runs{query}")
assert response.status_code == 200, f"Filter query failed: {query}"
# #endregion test_list_runs_filters
@@ -410,10 +412,10 @@ def test_list_runs_filters(mock_all_deps):
# #region test_list_runs_pagination [C:2] [TYPE Function]
# @BRIEF Pagination boundary checks on runs list.
def test_list_runs_pagination(mock_all_deps):
response = client.get("/api/validation/runs?page=0")
response = client.get("/api/validation-runs?page=0")
assert response.status_code == 422
response = client.get("/api/validation/runs?page_size=101")
response = client.get("/api/validation-runs?page_size=101")
assert response.status_code == 422
# #endregion test_list_runs_pagination
@@ -422,7 +424,7 @@ def test_list_runs_pagination(mock_all_deps):
# @BRIEF Status is a free string; any value passes through to service.
def test_list_runs_invalid_status(mock_all_deps):
mock_all_deps["run_service"].list_runs.return_value = (0, [])
response = client.get("/api/validation/runs?status=INVALID_STATUS_XYZ")
response = client.get("/api/validation-runs?status=INVALID_STATUS_XYZ")
assert response.status_code == 200
call_kwargs = mock_all_deps["run_service"].list_runs.call_args[1]
assert call_kwargs.get("status") == "INVALID_STATUS_XYZ"
@@ -432,7 +434,7 @@ def test_list_runs_invalid_status(mock_all_deps):
# #region test_get_run_detail_success [C:2] [TYPE Function]
# @BRIEF GET /validation/runs/{id} returns full detail with issues and raw_response.
def test_get_run_detail_success(mock_all_deps):
mock_all_deps["run_service"].get_run_detail.return_value = ValidationRunDetailResponse(
mock_all_deps["run_service"].get_run_detail.return_value = RunDetailResponse(
id="run-1",
task_id="task-1",
dashboard_id="dash-1",
@@ -446,13 +448,13 @@ def test_get_run_detail_success(mock_all_deps):
task_name="Test Task",
)
response = client.get("/api/validation/runs/run-1")
response = client.get("/api/validation-runs/run-1")
assert response.status_code == 200
data = response.json()
assert data["status"] == "FAIL"
assert len(data["issues"]) == 1
assert data["raw_response"] is not None
ValidationRunDetailResponse(**data)
RunDetailResponse(**data)
# #endregion test_get_run_detail_success
@@ -460,7 +462,7 @@ def test_get_run_detail_success(mock_all_deps):
# @BRIEF GET for nonexistent run returns 404.
def test_get_run_detail_not_found(mock_all_deps):
mock_all_deps["run_service"].get_run_detail.side_effect = ValueError("not found")
response = client.get("/api/validation/runs/nonexistent")
response = client.get("/api/validation-runs/nonexistent")
assert response.status_code == 404
# #endregion test_get_run_detail_not_found
@@ -469,7 +471,7 @@ def test_get_run_detail_not_found(mock_all_deps):
# @BRIEF DELETE /validation/runs/{id} returns 204.
def test_delete_run_success(mock_all_deps):
mock_all_deps["run_service"].delete_run.return_value = True
response = client.delete("/api/validation/runs/run-1")
response = client.delete("/api/validation-runs/run-1")
assert response.status_code == 204
mock_all_deps["run_service"].delete_run.assert_called_with("run-1")
# #endregion test_delete_run_success
@@ -479,7 +481,7 @@ def test_delete_run_success(mock_all_deps):
# @BRIEF DELETE for nonexistent run returns 404.
def test_delete_run_not_found(mock_all_deps):
mock_all_deps["run_service"].delete_run.return_value = False
response = client.delete("/api/validation/runs/nonexistent")
response = client.delete("/api/validation-runs/nonexistent")
assert response.status_code == 404
# #endregion test_delete_run_not_found
@@ -491,15 +493,15 @@ def test_delete_run_not_found(mock_all_deps):
def test_endpoints_require_authentication(mock_all_deps):
app.dependency_overrides.pop(get_current_user, None)
endpoints = [
("GET", "/api/validation/tasks"),
("POST", "/api/validation/tasks"),
("GET", "/api/validation/tasks/task-1"),
("PUT", "/api/validation/tasks/task-1"),
("DELETE", "/api/validation/tasks/task-1"),
("POST", "/api/validation/tasks/task-1/run"),
("GET", "/api/validation/runs"),
("GET", "/api/validation/runs/run-1"),
("DELETE", "/api/validation/runs/run-1"),
("GET", "/api/validation-tasks"),
("POST", "/api/validation-tasks"),
("GET", "/api/validation-tasks/task-1"),
("PUT", "/api/validation-tasks/task-1"),
("DELETE", "/api/validation-tasks/task-1"),
("POST", "/api/validation-tasks/task-1/run"),
("GET", "/api/validation-runs"),
("GET", "/api/validation-runs/run-1"),
("DELETE", "/api/validation-runs/run-1"),
]
for method, path in endpoints:
response = client.request(method, path)
@@ -512,7 +514,7 @@ def test_endpoints_require_authentication(mock_all_deps):
def test_permission_denied_non_admin(mock_all_deps):
"""Remove Admin role so has_permission raises 403."""
mock_user.roles = [] # No Admin, no permissions
response = client.get("/api/validation/tasks")
response = client.get("/api/validation-tasks")
assert response.status_code == 403
# #endregion test_permission_denied_non_admin
@@ -522,15 +524,15 @@ def test_permission_denied_non_admin(mock_all_deps):
def test_permission_denied_all_actions(mock_all_deps):
mock_user.roles = []
action_endpoints = [
("GET", "/api/validation/tasks"),
("POST", "/api/validation/tasks"),
("GET", "/api/validation/tasks/task-1"),
("PUT", "/api/validation/tasks/task-1"),
("DELETE", "/api/validation/tasks/task-1"),
("POST", "/api/validation/tasks/task-1/run"),
("GET", "/api/validation/runs"),
("GET", "/api/validation/runs/run-1"),
("DELETE", "/api/validation/runs/run-1"),
("GET", "/api/validation-tasks"),
("POST", "/api/validation-tasks"),
("GET", "/api/validation-tasks/task-1"),
("PUT", "/api/validation-tasks/task-1"),
("DELETE", "/api/validation-tasks/task-1"),
("POST", "/api/validation-tasks/task-1/run"),
("GET", "/api/validation-runs"),
("GET", "/api/validation-runs/run-1"),
("DELETE", "/api/validation-runs/run-1"),
]
for method, path in action_endpoints:
response = client.request(method, path)
@@ -542,7 +544,7 @@ def test_permission_denied_all_actions(mock_all_deps):
# @BRIEF delete_runs query param defaults to False.
def test_delete_task_runs_default_false(mock_all_deps):
mock_all_deps["task_service"].delete_task.return_value = None
response = client.delete("/api/validation/tasks/task-1")
response = client.delete("/api/validation-tasks/task-1")
assert response.status_code == 204
mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=False)
# #endregion test_delete_task_runs_default_false

View File

@@ -103,6 +103,7 @@ def _dict_to_run_response(d: dict[str, Any]) -> RunResponse:
# #region create_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new validation task.
# @RELATION CALLS -> [ValidationTaskService.create_task]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@@ -129,6 +130,7 @@ async def create_task(
# #region list_tasks [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List all validation tasks with pagination and optional filters.
# @RELATION CALLS -> [ValidationTaskService.list_tasks]
@router.get("", response_model=ValidationTaskListResponse)
@@ -159,6 +161,7 @@ async def list_tasks(
# #region get_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Get a validation task by policy ID.
# @RELATION CALLS -> [ValidationTaskService.get_task]
@router.get("/{policy_id}", response_model=ValidationTaskResponse)
@@ -181,6 +184,7 @@ async def get_task(
# #region update_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Update a validation task.
# @RELATION CALLS -> [ValidationTaskService.update_task]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@@ -208,6 +212,7 @@ async def update_task(
# #region delete_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a validation task.
# @RELATION CALLS -> [ValidationTaskService.delete_task]
# @RELATION CALLS -> [SchedulerService.remove_validation_job]
@@ -234,6 +239,7 @@ async def delete_task(
# #region toggle_task_status [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Toggle a validation task's active status.
# @RELATION CALLS -> [ValidationTaskService.update_task]
# @RELATION CALLS -> [SchedulerService.remove_validation_job]
@@ -270,6 +276,7 @@ async def toggle_task_status(
# #region trigger_run [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger immediate validation for a task.
# @RELATION CALLS -> [ValidationTaskService.trigger_run]
@router.post("/{policy_id}/run", response_model=TriggerRunResponse)
@@ -300,6 +307,7 @@ async def trigger_run(
# #region list_all_runs [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List validation runs across all tasks with optional filters.
# @RELATION CALLS -> [ValidationTaskService.list_all_runs]
@router.get("/runs/all", response_model=RecordListResponse)
@@ -330,6 +338,7 @@ async def list_all_runs(
# #region list_runs [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List run history for a validation task.
# @RELATION CALLS -> [ValidationTaskService.get_run_history]
@router.get("/{policy_id}/runs", response_model=RunListResponse)
@@ -356,6 +365,7 @@ async def list_runs(
# #region get_run_detail [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Get full run detail with all records for a validation task.
# @RELATION CALLS -> [ValidationTaskService.get_run_detail]
@router.get("/{policy_id}/runs/{run_id}", response_model=RunDetailResponse)
@@ -388,6 +398,7 @@ async def get_run_detail(
# #region parse_superset_url [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Parse a Superset dashboard URL and extract dashboard context, native filters, and recovery info.
# @RELATION CALLS -> [SupersetContextExtractor.parse_superset_link]
@router.post("/parse-url")
@@ -453,6 +464,7 @@ async def parse_superset_url(
# #region legacy_llm_report_redirect [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Legacy redirect: /reports/llm/{taskId} → /validation-tasks/{policy_id}/runs/{run_id}
# @RELATION CALLS -> [ValidationRun]
@router.get("/reports/llm/{task_id}")

View File

@@ -1,4 +1,5 @@
# #region AppModule [C:5] [TYPE Module] [SEMANTICS fastapi, websocket, startup, scheduler, middleware]
# @defgroup Module Module group.
# @BRIEF The main entry point for the FastAPI application.
# @LAYER API
# @RELATION DEPENDS_ON -> [ApiRoutesModule]
@@ -63,6 +64,7 @@ from .models.auth import Role, User
# #region lifespan [C:3] [TYPE Function]
# @ingroup Module
# @BRIEF Async context manager for FastAPI startup/shutdown lifecycle.
# @RELATION CALLS -> [init_db]
# @RELATION CALLS -> [AppDependencies]
@@ -113,6 +115,7 @@ async def lifespan(app: FastAPI):
scheduler.stop()
# #endregion lifespan
# #region FastAPI_App [C:3] [TYPE Global] [SEMANTICS app, fastapi, instance, route-registry]
# @ingroup Module
# @BRIEF Canonical FastAPI application instance for route, middleware, and websocket registration.
# @RELATION DEPENDS_ON -> [ApiRoutesModule]
# @RELATION BINDS_TO -> [API_Routes]
@@ -133,6 +136,7 @@ from .core.middleware.trace import TraceContextMiddleware # noqa: E402
app.add_middleware(TraceContextMiddleware)
# #endregion FastAPI_App
# #region ensure_initial_admin_user [C:3] [TYPE Function]
# @ingroup Module
# @BRIEF Ensures initial admin user exists when bootstrap env flags are enabled.
# @RELATION DEPENDS_ON -> [AuthRepository]
def ensure_initial_admin_user() -> None:
@@ -187,6 +191,7 @@ def ensure_initial_admin_user() -> None:
db.close()
# #endregion ensure_initial_admin_user
# #region run_alembic_migrations [C:2] [TYPE Function]
# @ingroup Module
# @BRIEF Applies all pending Alembic migrations against DATABASE_URL.
# DEPRECATED: Migrations now run exclusively in docker/backend.entrypoint.sh.
# Kept for local development (manual invocation).
@@ -217,6 +222,7 @@ def run_alembic_migrations() -> None:
# #region app_middleware [TYPE Block]
# @ingroup Module
# @BRIEF Configure application-wide middleware (Session, CORS).
# @RATIONALE SessionMiddleware uses SESSION_SECRET_KEY independent of JWT SECRET_KEY (see [SEC:H-4]).
# CORS allow_origins crashes early if ALLOWED_ORIGINS is unset — no "*" fallback (see [SEC:M-1]).
@@ -277,6 +283,7 @@ class HSTSMiddleware(BaseHTTPMiddleware):
app.add_middleware(HSTSMiddleware)
# #endregion app_middleware
# #region global_exception_handler [C:2] [TYPE Function]
# @ingroup Module
# @BRIEF Global exception handler — logs all unhandled 500 errors into the app logger.
# @PRE request is a FastAPI Request object.
# @POST Logs full traceback to superset_tools_app logger; returns 500.
@@ -317,6 +324,7 @@ async def network_error_handler(request: Request, exc: NetworkError):
)
# #endregion network_error_handler
# #region log_requests [C:3] [TYPE Function]
# @ingroup Module
# @BRIEF Middleware to log incoming HTTP requests and their response status.
# @RELATION DEPENDS_ON -> [LoggerModule]
# @PRE request is a FastAPI Request object.
@@ -358,6 +366,7 @@ async def log_requests(request: Request, call_next):
)
# #endregion log_requests
# #region API_Routes [C:3] [TYPE Block]
# @ingroup Module
# @BRIEF Register all FastAPI route groups exposed by the application entrypoint.
# @RELATION DEPENDS_ON -> [FastAPI_App]
# @RELATION DEPENDS_ON -> [Route_Group_Contracts]
@@ -465,6 +474,7 @@ async def _authenticate_websocket(websocket: WebSocket, endpoint_name: str) -> b
# #endregion _authenticate_websocket
# #region websocket_endpoint [C:5] [TYPE Function]
# @ingroup Module
# @BRIEF Provides a WebSocket endpoint for real-time log streaming of a task with server-side filtering.
# @RELATION CALLS -> [TaskManagerPackage]
# @RELATION DEPENDS_ON -> [LoggerModule]
@@ -688,6 +698,7 @@ async def websocket_endpoint(
# #endregion websocket_endpoint
# #region task_events_websocket [C:4] [TYPE Function]
# @ingroup Module
# @BRIEF WebSocket endpoint for global task events (status changes for ALL tasks).
# @RELATION CALLS -> [TaskManagerPackage]
# @PRE WebSocket must be authenticated via `token` query param.
@@ -735,6 +746,7 @@ async def task_events_websocket(websocket: WebSocket):
# #endregion task_events_websocket
# #region maintenance_events_websocket [C:4] [TYPE Function]
# @ingroup Module
# @BRIEF WebSocket endpoint for maintenance events (created/ended/banner changes).
# @RELATION CALLS -> [TaskManagerPackage]
# @PRE WebSocket must be authenticated via `token` query param.
@@ -782,6 +794,7 @@ async def maintenance_events_websocket(websocket: WebSocket):
# #endregion maintenance_events_websocket
# #region dataset_websocket_endpoint [C:4] [TYPE Function]
# @ingroup Module
# @BRIEF WebSocket endpoint for dataset.updated events — auto-refresh on task completion.
# @RELATION CALLS -> [TaskManagerPackage]
# @PRE env_id must reference a known environment.
@@ -815,6 +828,7 @@ async def dataset_websocket_endpoint(websocket: WebSocket, env_id: str):
logger.reflect("Released dataset event subscription", extra={"env_id": env_id})
# #endregion dataset_websocket_endpoint
# #region translate_run_websocket [C:3] [TYPE Function]
# @ingroup Module
# @BRIEF WebSocket endpoint for translation run progress — streams structured status updates.
# @PRE run_id must be a valid translation run ID. WebSocket authenticated via `token` query param.
# @POST Streams run status JSON every second until terminal state or disconnect.

View File

@@ -1,3 +1,4 @@
# #region src.core [TYPE Package] [SEMANTICS core, package, init]
# @ingroup Core
# @BRIEF Backend core services and infrastructure package root.
# #endregion src.core

View File

@@ -12,7 +12,7 @@ import requests
from src.core.config_models import Environment
from src.core.superset_client import SupersetClient
from src.core.utils.async_network import AsyncAPIClient
from src.core.utils.network import APIClient, DashboardNotFoundError, SupersetAPIError
from src.core.utils.network import DashboardNotFoundError, SupersetAPIError
# #region _make_environment [TYPE Function]
@@ -376,41 +376,7 @@ def test_build_dataset_preview_legacy_form_data_preserves_native_filter_clauses(
assert legacy_form_data["url_params"] == {"country": "DE"}
assert legacy_form_data["result_type"] == "query"
# #endregion test_build_dataset_preview_legacy_form_data_preserves_native_filter_clauses
# #region test_sync_network_404_mapping_keeps_non_dashboard_endpoints_generic [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Sync network client should reserve dashboard-not-found translation for dashboard endpoints only.
def test_sync_network_404_mapping_keeps_non_dashboard_endpoints_generic():
client = APIClient(
config={
"base_url": "http://superset.local",
"auth": {"username": "demo", "password": "secret"},
}
)
with pytest.raises(SupersetAPIError) as exc_info:
client._handle_http_error(
_make_requests_http_error(404, "http://superset.local/api/v1/chart/data"),
"/chart/data",
)
assert not isinstance(exc_info.value, DashboardNotFoundError)
assert "API resource not found at endpoint '/chart/data'" in str(exc_info.value)
# #endregion test_sync_network_404_mapping_keeps_non_dashboard_endpoints_generic
# #region test_sync_network_404_mapping_translates_dashboard_endpoints [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Sync network client should still translate dashboard endpoint 404 responses into dashboard-not-found errors.
def test_sync_network_404_mapping_translates_dashboard_endpoints():
client = APIClient(
config={
"base_url": "http://superset.local",
"auth": {"username": "demo", "password": "secret"},
}
)
with pytest.raises(DashboardNotFoundError) as exc_info:
client._handle_http_error(
_make_requests_http_error(404, "http://superset.local/api/v1/dashboard/10"),
"/dashboard/10",
)
assert "Dashboard '/dashboard/10' Dashboard not found" in str(exc_info.value)
# #endregion test_sync_network_404_mapping_translates_dashboard_endpoints
# #region test_async_network_404_mapping_keeps_non_dashboard_endpoints_generic [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Async network client should reserve dashboard-not-found translation for dashboard endpoints only.

View File

@@ -1,4 +1,5 @@
# #region AsyncSupersetClientModule [C:3] [TYPE Module] [SEMANTICS superset, transform, dashboard, filter, async-superset-client]
# @defgroup Core Module group.
# @BRIEF Backward-compatible alias for the async-migrated SupersetClient.
# @DEPRECATED 2026-06-04 — class kept as thin compat wrapper. New code should import SupersetClient directly.
# @REPLACED_BY -> [SupersetClient]
@@ -13,6 +14,7 @@ from .utils.async_network import AsyncAPIClient
# #region AsyncSupersetClient [C:3] [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Backward-compatible alias for the async-migrated SupersetClient.
# @RELATION INHERITS -> [SupersetClient]
# @RELATION DEPENDS_ON -> [AsyncAPIClient]
@@ -25,6 +27,7 @@ class AsyncSupersetClient(SupersetClient):
"""
# #region AsyncSupersetClientInit [TYPE Function] [C:2]
# @ingroup Core
# @PURPOSE: Initialize async Superset client — delegates to SupersetClient.
# @PRE env is valid Environment instance.
# @POST Client uses AsyncAPIClient transport.
@@ -35,6 +38,7 @@ class AsyncSupersetClient(SupersetClient):
# #endregion AsyncSupersetClientInit
# #region AsyncSupersetClientClose [TYPE Function] [C:2]
# @ingroup Core
# @PURPOSE: Close async transport resources.
# @POST Underlying AsyncAPIClient is closed.
# @SIDE_EFFECT Closes network sockets.

View File

@@ -1,3 +1,4 @@
# #region AuthPackage [TYPE Package] [SEMANTICS auth, package, init]
# @ingroup Auth
# @BRIEF Authentication and authorization package root.
# #endregion AuthPackage

View File

@@ -1,4 +1,5 @@
# #region APIKeyUtilities [C:2] [TYPE Module] [SEMANTICS auth, api_key, crypto, generation]
# @defgroup Auth Module group.
# @BRIEF API key generation and hashing utilities for service-to-service authentication.
# @LAYER Core
# @RELATION DEPENDS_ON -> [EXT:Python:hashlib]
@@ -11,6 +12,7 @@ import secrets
# #region generate_api_key [C:2] [TYPE Function]
# @ingroup Auth
# @BRIEF Generate a new API key in ssk_ format with SHA-256 hash.
# @POST Returns (raw_key, prefix, key_hash) — raw_key shown ONCE to caller, never stored.
# @SIDE_EFFECT Uses secrets.token_urlsafe(32) for cryptographic randomness.

View File

@@ -1,4 +1,5 @@
# #region AuthConfigModule [C:2] [TYPE Module] [SEMANTICS pydantic, auth, auth-config, config]
# @defgroup Auth Module group.
#
# @BRIEF Centralized configuration for authentication and authorization.
# @LAYER Core
@@ -17,6 +18,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
# #region AuthConfig [TYPE Class]
# @defgroup Auth Module group.
# @BRIEF Holds authentication-related settings.
# @PRE Environment variables may be provided via .env file.
# @POST Returns a configuration object with validated settings.
@@ -65,6 +67,7 @@ class AuthConfig(BaseSettings):
# #endregion AuthConfig
# #region auth_config [TYPE Variable]
# @ingroup Auth
# @BRIEF Singleton instance of AuthConfig.
# @RELATION DEPENDS_ON -> AuthConfig
auth_config = AuthConfig()

View File

@@ -1,16 +1,15 @@
# #region AuthJwtModule [C:5] [TYPE Module] [SEMANTICS auth, validate, jwt, token]
#
# @BRIEF JWT token generation and validation logic with server-side revocation.
# #region Auth.Jwt [C:5] [TYPE Module] [SEMANTICS auth,jwt,token,security]
# @defgroup Auth JWT token generation, validation, and revocation.
# @LAYER Core
# @RELATION DEPENDS_ON -> [auth_config]
# @RELATION DEPENDS_ON -> [TokenBlacklist]
#
# @INVARIANT Tokens must include aud, iss, iat, jti, exp, and sub claims.
# Revoked tokens are rejected even if not expired.
# @PRE JWT secret configured in environment
# @POST Token encode/decode/revoke functions exported
# @SIDE_EFFECT None
# @DATA_CONTRACT TokenPayload -> JWT string
# @RELATION DEPENDS_ON -> [Auth.Config]
# @RELATION DEPENDS_ON -> [Auth.TokenBlacklist]
# @INVARIANT Tokens must include aud, iss, iat, jti, exp, and sub claims.
# @INVARIANT Revoked tokens are rejected even if not expired.
# @PRE JWT secret configured in environment.
# @POST Token encode/decode/revoke functions exported.
# @DATA_CONTRACT TokenPayload -> JWT string
# @RATIONALE JWT chosen over opaque tokens for stateless validation — eliminates DB lookup on every request. Server-side blacklist added for immediate revocation (logout, password change).
# @REJECTED Session-based auth rejected — requires server-side state per request. Opaque tokens rejected — every validation requires DB round-trip.
from datetime import UTC, datetime, timedelta
import hashlib
@@ -24,11 +23,13 @@ from ..logger import belief_scope
from .config import auth_config
# #region create_access_token [TYPE Function]
# #region Auth.Jwt.CreateAccessToken [C:4] [TYPE Function] [SEMANTICS auth,jwt,token]
# @ingroup Auth
# @BRIEF Generates a new JWT access token with aud, iss, iat, jti, exp claims.
# @PRE data dict contains 'sub' (user_id) and optional 'scopes' (roles).
# @POST Returns a signed JWT string with unique jti for revocation.
# @RELATION DEPENDS_ON -> [auth_config]
# @PRE data dict contains 'sub' (user_id) and optional 'scopes'.
# @POST Returns a signed JWT string with unique jti for revocation.
# @SIDE_EFFECT None (pure function).
# @RELATION DEPENDS_ON -> [Auth.Config]
#
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
with belief_scope("create_access_token"):
@@ -54,14 +55,16 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None) -> s
return encoded_jwt
# #endregion create_access_token
# #endregion Auth.Jwt.CreateAccessToken
# #region decode_token [TYPE Function]
# #region Auth.Jwt.DecodeToken [C:4] [TYPE Function] [SEMANTICS auth,jwt,validate]
# @ingroup Auth
# @BRIEF Decodes and validates a JWT token — verifies aud, iss, exp, and blacklist.
# @PRE token is a signed JWT string.
# @POST Returns the decoded payload if valid. Raises JWTError if invalid.
# @RELATION DEPENDS_ON -> [auth_config]
# @PRE token is a signed JWT string.
# @POST Returns decoded payload if valid. Raises JWTError if invalid/revoked.
# @RELATION DEPENDS_ON -> [Auth.Config]
# @RELATION CALLS -> [Auth.Jwt.IsTokenBlacklisted]
#
def decode_token(token: str) -> dict:
with belief_scope("decode_token"):
@@ -81,39 +84,41 @@ def decode_token(token: str) -> dict:
return payload
# #endregion decode_token
# #endregion Auth.Jwt.DecodeToken
# #region _hash_token [TYPE Function]
# #region Auth.Jwt._HashToken [C:2] [TYPE Function]
# @BRIEF SHA-256 hash of a token string for blacklist lookup.
# @PRE token is a non-empty JWT string.
# @POST Returns 64-character hex digest.
# @PRE token is a non-empty JWT string.
# @POST Returns 64-character hex digest.
def _hash_token(token: str) -> str:
return hashlib.sha256(token.encode()).hexdigest()
# #endregion _hash_token
# #endregion Auth.Jwt._HashToken
# #region _prune_blacklist [TYPE Function]
# #region Auth.Jwt._PruneBlacklist [C:2] [TYPE Function]
# @BRIEF Delete expired entries from the token blacklist.
# @PRE db is a valid SQLAlchemy session.
# @POST Expired blacklist rows are removed.
# @PRE db is a valid SQLAlchemy session.
# @POST Expired blacklist rows are removed.
# @SIDE_EFFECT Mutates token_blacklist table.
def _prune_blacklist(db: Session) -> None:
now = datetime.now(UTC)
db.query(TokenBlacklist).filter(TokenBlacklist.expires_at < now).delete()
db.commit()
# #endregion _prune_blacklist
# #endregion Auth.Jwt._PruneBlacklist
# #region blacklist_token [TYPE Function]
# #region Auth.Jwt.BlacklistToken [C:4] [TYPE Function] [SEMANTICS auth,jwt,revoke]
# @ingroup Auth
# @BRIEF Revoke a JWT token by adding its hash to the blacklist.
# @PRE token was issued by this service and hasn't expired.
# @POST Token is blacklisted and subsequent decode_token calls will reject it.
# @SIDE_EFFECT Writes to token_blacklist table; prunes expired entries.
# @RELATION DEPENDS_ON -> [TokenBlacklist]
# @PRE token was issued by this service and hasn't expired.
# @POST Token is blacklisted; subsequent decode attempts will reject it.
# @SIDE_EFFECT Writes to token_blacklist table; prunes expired entries.
# @RELATION DEPENDS_ON -> [Auth.TokenBlacklist]
# @RELATION CALLS -> [Auth.Jwt.HashToken]
# @TEST_EDGE: already_expired -> skips blacklisting
def blacklist_token(token: str, db: Session) -> None:
with belief_scope("blacklist_token"):
_prune_blacklist(db)
@@ -145,14 +150,15 @@ def blacklist_token(token: str, db: Session) -> None:
db.commit()
# #endregion blacklist_token
# #endregion Auth.Jwt.BlacklistToken
# #region is_token_blacklisted [TYPE Function]
# @BRIEF Check if a JWT token has been revoked.
# @PRE db is a valid SQLAlchemy session.
# @POST Returns True if token is blacklisted, False otherwise.
# @RELATION DEPENDS_ON -> [TokenBlacklist]
# #region Auth.Jwt.IsTokenBlacklisted [C:2] [TYPE Function] [SEMANTICS auth,jwt,revoke]
# @ingroup Auth
# @BRIEF Check if a JWT token hash exists in the blacklist.
# @PRE db is a valid SQLAlchemy session.
# @POST Returns True if token is blacklisted, False otherwise.
# @RELATION DEPENDS_ON -> [Auth.TokenBlacklist]
def is_token_blacklisted(token: str, db: Session) -> bool:
with belief_scope("is_token_blacklisted"):
try:
@@ -167,6 +173,6 @@ def is_token_blacklisted(token: str, db: Session) -> bool:
return db.query(TokenBlacklist).filter(TokenBlacklist.jti == jti).first() is not None
# #endregion is_token_blacklisted
# #endregion Auth.Jwt.IsTokenBlacklisted
# #endregion AuthJwtModule
# #endregion Auth.Jwt

View File

@@ -1,4 +1,5 @@
# #region AuthLoggerModule [C:5] [TYPE Module] [SEMANTICS auth, audit, logging]
# @defgroup Auth Module group.
#
# @BRIEF Structured auth logging module for audit trail generation.
# @LAYER Core
@@ -41,6 +42,7 @@ def _mask_details(details: dict[str, Any] | None) -> dict[str, Any] | None:
# #region log_security_event [TYPE Function]
# @ingroup Auth
# @BRIEF Logs a security-related event for audit trails.
# @PRE event_type and username are strings.
# @POST Security event is written to the application log via %s-formatting to prevent log injection.

View File

@@ -1,4 +1,5 @@
# #region AuthOauthModule [C:2] [TYPE Module] [SEMANTICS auth, oauth, oidc, adfs, authlib]
# @defgroup Auth Module group.
#
# @BRIEF ADFS OIDC configuration and client using Authlib.
# @LAYER Core
@@ -12,12 +13,14 @@ from authlib.integrations.starlette_client import OAuth
from .config import auth_config
# #region oauth [TYPE Variable]
# @ingroup Auth
# @BRIEF Global Authlib OAuth registry.
# @RELATION DEPENDS_ON -> [EXT:Library:OAuth]
oauth = OAuth()
# #endregion oauth
# #region register_adfs [TYPE Function]
# @ingroup Auth
# @BRIEF Registers the ADFS OIDC client.
# @PRE ADFS configuration is provided in auth_config.
# @POST ADFS client is registered in oauth registry.
@@ -37,6 +40,7 @@ def register_adfs():
# #endregion register_adfs
# #region is_adfs_configured [TYPE Function]
# @ingroup Auth
# @BRIEF Checks if ADFS is properly configured.
# @PRE None.
# @POST Returns True if ADFS client is registered, False otherwise.

View File

@@ -1,4 +1,5 @@
# #region AuthRepositoryModule [C:5] [TYPE Module] [SEMANTICS sqlalchemy, auth, search, user, auth-repository]
# @defgroup Auth Module group.
# @BRIEF Data access layer for authentication and user preference entities.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [AuthModels]
@@ -18,6 +19,7 @@ from ..logger import belief_scope, logger
# #region AuthRepository [TYPE Class]
# @defgroup Auth Module group.
# @BRIEF Provides low-level CRUD operations for identity and authorization records.
# @PRE Database session is bound.
# @POST Entity instances returned safely.
@@ -27,6 +29,7 @@ class AuthRepository:
def __init__(self, db: Session):
self.db = db
# #region get_user_by_id [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve user by UUID.
# @PRE user_id is a valid UUID string.
# @POST Returns User object if found, else None.
@@ -39,6 +42,7 @@ class AuthRepository:
return result
# #endregion get_user_by_id
# #region get_user_by_username [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve user by username.
# @PRE username is a non-empty string.
# @POST Returns User object if found, else None.
@@ -51,6 +55,7 @@ class AuthRepository:
return result
# #endregion get_user_by_username
# #region get_role_by_id [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve role by UUID with permissions preloaded.
# @RELATION DEPENDS_ON -> [Role]
# @RELATION DEPENDS_ON -> [Permission]
@@ -64,6 +69,7 @@ class AuthRepository:
)
# #endregion get_role_by_id
# #region get_role_by_name [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve role by unique name.
# @RELATION DEPENDS_ON -> [Role]
def get_role_by_name(self, name: str) -> Role | None:
@@ -71,6 +77,7 @@ class AuthRepository:
return self.db.query(Role).filter(Role.name == name).first()
# #endregion get_role_by_name
# #region get_permission_by_id [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve permission by UUID.
# @RELATION DEPENDS_ON -> [Permission]
def get_permission_by_id(self, permission_id: str) -> Permission | None:
@@ -80,6 +87,7 @@ class AuthRepository:
)
# #endregion get_permission_by_id
# #region get_permission_by_resource_action [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve permission by resource and action tuple.
# @RELATION DEPENDS_ON -> [Permission]
def get_permission_by_resource_action(
@@ -93,6 +101,7 @@ class AuthRepository:
)
# #endregion get_permission_by_resource_action
# #region list_permissions [TYPE Function]
# @ingroup Auth
# @PURPOSE: List all system permissions.
# @RELATION DEPENDS_ON -> [Permission]
def list_permissions(self) -> list[Permission]:
@@ -100,6 +109,7 @@ class AuthRepository:
return self.db.query(Permission).all()
# #endregion list_permissions
# #region get_user_dashboard_preference [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve dashboard filters/preferences for a user.
# @RELATION DEPENDS_ON -> [UserDashboardPreference]
def get_user_dashboard_preference(
@@ -113,6 +123,7 @@ class AuthRepository:
)
# #endregion get_user_dashboard_preference
# #region get_roles_by_ad_groups [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve roles that match a list of AD group names.
# @PRE groups is a list of strings representing AD group identifiers.
# @POST Returns a list of Role objects mapped to the provided AD groups.

View File

@@ -1,4 +1,5 @@
# #region AuthSecurityModule [C:2] [TYPE Module] [SEMANTICS auth, password, hashing, bcrypt, security]
# @defgroup Auth Module group.
#
# @BRIEF Utility for password hashing and verification using Passlib.
# @LAYER Core
@@ -10,6 +11,7 @@ import bcrypt
# #region verify_password [TYPE Function]
# @ingroup Auth
# @BRIEF Verifies a plain password against a hashed password.
# @PRE plain_password is a string, hashed_password is a bcrypt hash.
# @POST Returns True if password matches, False otherwise.
@@ -28,6 +30,7 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
# #endregion verify_password
# #region get_password_hash [TYPE Function]
# @ingroup Auth
# @BRIEF Generates a bcrypt hash for a plain password.
# @PRE password is a string.
# @POST Returns a secure bcrypt hash string.

View File

@@ -1,4 +1,5 @@
# #region ConfigManager [C:5] [TYPE Module] [SEMANTICS sqlalchemy, validate, migration, config-manager]
# @defgroup Core Module group.
#
# @BRIEF Manages application configuration persistence in DB with one-time migration from legacy JSON.
# @LAYER Domain
@@ -31,6 +32,7 @@ from .logger import belief_scope, configure_logger, logger
# #region ConfigManager [C:5] [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Handles application configuration load, validation, mutation, and persistence lifecycle.
# @PRE Database is accessible and AppConfigRecord schema is loaded.
# @POST Configuration state is synchronized between memory and database.
@@ -446,6 +448,7 @@ class ConfigManager:
db.close()
# #endregion _save_config_to_db
# #region save [TYPE Function]
# @ingroup Core
# @PURPOSE: Persist current in-memory configuration state.
def save(self) -> None:
with belief_scope("ConfigManager.save"):
@@ -453,18 +456,21 @@ class ConfigManager:
self._save_config_to_db(self.config)
# #endregion save
# #region get_config [TYPE Function]
# @ingroup Core
# @PURPOSE: Return current in-memory configuration snapshot.
def get_config(self) -> AppConfig:
with belief_scope("ConfigManager.get_config"):
return self.config
# #endregion get_config
# #region get_payload [TYPE Function]
# @ingroup Core
# @PURPOSE: Return full persisted payload including sections outside typed AppConfig schema.
def get_payload(self) -> dict[str, Any]:
with belief_scope("ConfigManager.get_payload"):
return self._sync_raw_payload_from_config()
# #endregion get_payload
# #region save_config [TYPE Function]
# @ingroup Core
# @PURPOSE: Persist configuration provided either as typed AppConfig or raw payload dict.
def save_config(self, config: Any) -> AppConfig:
with belief_scope("ConfigManager.save_config"):
@@ -496,6 +502,7 @@ class ConfigManager:
raise TypeError("config must be AppConfig or dict")
# #endregion save_config
# #region update_global_settings [TYPE Function]
# @ingroup Core
# @PURPOSE: Replace global settings and persist the resulting configuration.
def update_global_settings(self, settings: GlobalSettings) -> AppConfig:
with belief_scope("ConfigManager.update_global_settings"):
@@ -505,6 +512,7 @@ class ConfigManager:
return self.config
# #endregion update_global_settings
# #region validate_path [TYPE Function]
# @ingroup Core
# @PURPOSE: Validate that path exists and is writable, creating it when absent.
def validate_path(self, path: str) -> tuple[bool, str]:
with belief_scope("ConfigManager.validate_path", f"path={path}"):
@@ -528,18 +536,21 @@ class ConfigManager:
return False, str(exc)
# #endregion validate_path
# #region get_environments [TYPE Function]
# @ingroup Core
# @PURPOSE: Return all configured environments.
def get_environments(self) -> list[Environment]:
with belief_scope("ConfigManager.get_environments"):
return list(self.config.environments)
# #endregion get_environments
# #region has_environments [TYPE Function]
# @ingroup Core
# @PURPOSE: Check whether at least one environment exists in configuration.
def has_environments(self) -> bool:
with belief_scope("ConfigManager.has_environments"):
return len(self.config.environments) > 0
# #endregion has_environments
# #region get_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Resolve a configured environment by identifier.
def get_environment(self, env_id: str) -> Environment | None:
with belief_scope("ConfigManager.get_environment", f"env_id={env_id}"):
@@ -552,6 +563,7 @@ class ConfigManager:
return None
# #endregion get_environment
# #region add_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Upsert environment by id into configuration and persist.
def add_environment(self, env: Environment) -> AppConfig:
with belief_scope("ConfigManager.add_environment", f"env_id={env.id}"):
@@ -583,6 +595,7 @@ class ConfigManager:
return self.config
# #endregion add_environment
# #region update_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Update existing environment by id and preserve masked password placeholder behavior.
def update_environment(self, env_id: str, env: Environment) -> bool:
with belief_scope("ConfigManager.update_environment", f"env_id={env_id}"):
@@ -608,6 +621,7 @@ class ConfigManager:
return False
# #endregion update_environment
# #region delete_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Delete environment by id and persist when deletion occurs.
def delete_environment(self, env_id: str) -> bool:
with belief_scope("ConfigManager.delete_environment", f"env_id={env_id}"):

View File

@@ -1,14 +1,17 @@
# #region ConfigModels [C:3] [TYPE Module] [SEMANTICS pydantic, model, schedule]
# @defgroup Core Module group.
# @BRIEF Defines the data models for application configuration using Pydantic.
# @LAYER Core
# @RELATION IMPLEMENTS -> [CoreContracts]
# @RELATION IMPLEMENTS -> [ConnectionContracts]
#
# #region CoreContracts [C:2] [TYPE Interface]
# @ingroup Core
# @BRIEF Contract for core configuration models — Pydantic BaseModel subclasses with scheduler config.
# #endregion CoreContracts
#
# #region ConnectionContracts [C:2] [TYPE Interface]
# @ingroup Core
# @BRIEF Contract for database/environment connection configuration models.
# #endregion ConnectionContracts
@@ -23,6 +26,7 @@ from ..services.llm_prompt_templates import (
# #region Schedule [TYPE DataClass]
# @ingroup Core
# @BRIEF Represents a backup schedule configuration.
class Schedule(BaseModel):
enabled: bool = False
@@ -33,6 +37,7 @@ class Schedule(BaseModel):
# #region Environment [TYPE DataClass]
# @ingroup Core
# @BRIEF Represents a Superset environment configuration with async connection pool settings.
class Environment(BaseModel):
id: str
@@ -70,6 +75,7 @@ class Environment(BaseModel):
# #region AppAsyncRuntimeConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF Global application-level async runtime configuration.
# @RATIONALE Separated from EnvironmentConfig because these settings are not per-env:
# executor workers, shutdown timeout, blocking queue timeout are app-wide.
@@ -104,6 +110,7 @@ class AppAsyncRuntimeConfig(BaseModel):
# #region LoggingConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF Defines the configuration for the application's logging system.
class LoggingConfig(BaseModel):
level: str = "INFO"
@@ -120,6 +127,7 @@ class LoggingConfig(BaseModel):
# #region CleanReleaseConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF Configuration for clean release compliance subsystem.
class CleanReleaseConfig(BaseModel):
active_policy_id: str | None = None
@@ -142,6 +150,7 @@ class FeaturesConfig(BaseModel):
# #region GlobalSettings [TYPE DataClass]
# @ingroup Core
# @BRIEF Represents global application settings.
class GlobalSettings(BaseModel):
storage: StorageConfig = Field(default_factory=StorageConfig)
@@ -218,6 +227,7 @@ class GlobalSettings(BaseModel):
# #region AppConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF The root configuration model containing all application settings.
class AppConfig(BaseModel):
environments: list[Environment] = []

Some files were not shown because too many files have changed in this diff Show More