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 db16886ce4
commit b5e741077d
562 changed files with 2523 additions and 244 deletions

138
add_defgroup_ingroup.py Normal file
View File

@@ -0,0 +1,138 @@
# #region AddDefgroupIngroup [C:3] [TYPE Module] [SEMANTICS migration,defgroup,ingroup]
# @defgroup Migration Add @defgroup/@ingroup — zero-risk additive operation.
# @BRIEF Inserts one line after each #region anchor. No renames. No deletions. Bottom-up insertion.
# @RATIONALE 97.9% of contracts lack @ingroup — the model's strongest pre-training DSA signal.
# @INVARIANT Anchor pairs untouched. @RELATION edges untouched. Business logic untouched.
import re, sys
from pathlib import Path
PATH_TO_DOMAIN = [
("frontend/src/lib/models/", "Models"), ("frontend/src/lib/components/ui/", "UI"),
("frontend/src/lib/components/translate/", "Translate"), ("frontend/src/lib/components/dashboard/", "Dashboard"),
("frontend/src/lib/components/llm/", "LLM"), ("frontend/src/lib/components/dataset-review/", "DatasetReview"),
("frontend/src/lib/components/settings/", "Settings"), ("frontend/src/lib/components/layout/", "Layout"),
("frontend/src/lib/components/assistant/", "Assistant"), ("frontend/src/lib/components/tasks/", "Tasks"),
("frontend/src/lib/components/", "Components"), ("frontend/src/lib/ui/", "UI"),
("frontend/src/lib/stores/", "Stores"), ("frontend/src/lib/api/", "ApiClient"),
("frontend/src/routes/", "Routes"), ("frontend/src/types/", "Types"),
("backend/src/api/routes/assistant/", "AssistantApi"), ("backend/src/api/routes/", "Api"),
("backend/src/core/task_manager/", "TaskManager"), ("backend/src/core/auth/", "Auth"),
("backend/src/core/migration/", "Migration"), ("backend/src/core/plugins/", "Plugin"),
("backend/src/core/", "Core"), ("backend/src/services/dataset_review/", "DatasetReview"),
("backend/src/services/", "Services"), ("backend/src/models/", "Models"),
("backend/src/schemas/", "Schemas"), ("backend/src/plugins/translate/", "Translate"),
("backend/src/plugins/llm_analysis/", "LLMAnalysis"), ("backend/src/plugins/git/", "Git"),
("backend/src/plugins/storage/", "Storage"), ("backend/src/plugins/", "Plugin"),
("backend/tests/", "Tests"),
]
SEMANTICS_TO_DOMAIN = {
"auth": "Auth", "login": "Auth", "token": "Auth", "migration": "Migration",
"dashboard": "Dashboard", "dataset": "Dataset", "translate": "Translate",
"translation": "Translate", "llm": "LLM", "git": "Git", "storage": "Storage",
"plugin": "Plugin", "task": "Tasks", "test": "Tests", "api": "Api", "ui": "UI",
"users": "Users", "roles": "Roles", "settings": "Settings", "profile": "Profile",
"reports": "Reports", "validation": "Validation", "backup": "Backup",
"mapper": "Mapper", "debug": "Debug", "assistant": "Assistant", "admin": "Admin",
"notification": "Notifications", "schedule": "Schedule", "logging": "Logging",
"indexing": "Index", "curation": "Curator", "semantic": "Semantic",
"screenshot": "Screenshot", "health": "Health",
}
ANCHOR_RE = re.compile(
r'^(\s*(?:#|//|<!--)\s*#region\s+(?P<id>[^\s\[\(]+)\s*\[[^\]]+\][^\n]*)', re.MULTILINE)
INGROUP_RE = re.compile(r'@ingroup\s', re.MULTILINE)
DEFGROUP_RE = re.compile(r'@defgroup\s', re.MULTILINE)
SEMANTICS_RE = re.compile(r'@SEMANTICS\s+([^\n]+)')
def find_files(root):
dirs = [root / d for d in ["backend/src", "frontend/src", "backend/tests"]]
return sorted({f for d in dirs if d.exists() for f in d.rglob("*.py")} |
{f for d in dirs if d.exists() for f in d.rglob("*.svelte")} |
{f for d in dirs if d.exists() for f in d.rglob("*.svelte.ts")})
def infer_domain(fp, semantics):
for kw in semantics:
kw = kw.strip().lower()
if kw in SEMANTICS_TO_DOMAIN:
return SEMANTICS_TO_DOMAIN[kw]
s = str(fp)
for prefix, domain in PATH_TO_DOMAIN:
if prefix in s:
return domain
return "Module"
def process(fp, dry_run=True):
content = fp.read_text(encoding="utf-8")
insertions = [] # (byte_position, text_to_insert)
for m in ANCHOR_RE.finditer(content):
anchor_line = m.group(0)
cid = m.group("id")
pos = m.end() # right after anchor line
# Skip checks
if cid.startswith("_"):
continue
if "[C:1]" in anchor_line:
continue
if "tests" in str(fp).lower() or "__tests__" in str(fp):
continue
# Check nearby text for existing @ingroup/@defgroup
nearby = content[pos:pos + 600]
if INGROUP_RE.search(nearby) or DEFGROUP_RE.search(nearby):
continue
sem = SEMANTICS_RE.search(content[m.start():pos + 600])
semantics = [s.strip() for s in sem.group(1).split(",")] if sem else []
domain = infer_domain(fp, semantics)
stripped = anchor_line.strip()
if stripped.startswith("<!--"):
prefix, suffix = "<!-- ", " -->"
elif stripped.startswith("//"):
prefix, suffix = "// ", ""
else:
prefix, suffix = "# ", ""
is_mod = "[TYPE Module]" in anchor_line or "[TYPE Class]" in anchor_line
line = f"\n{prefix}@defgroup {domain} Module group.{suffix}" if is_mod else f"\n{prefix}@ingroup {domain}{suffix}"
insertions.append((pos, line))
# Apply bottom-up
new_content = content
for pos, line in sorted(insertions, reverse=True):
new_content = new_content[:pos] + line + new_content[pos:]
if insertions and not dry_run:
fp.write_text(new_content, encoding="utf-8")
return {"file": str(fp), "added": len(insertions)}
def main():
dry = "--apply" not in sys.argv
root = Path.cwd()
for a in sys.argv[1:]:
if a.startswith("--root="):
root = Path(a.split("=", 1)[1])
print(f"=== @defgroup/@ingroup — {'DRY RUN' if dry else 'APPLY'} ===")
files = find_files(root)
total, results = 0, []
for f in files:
r = process(f, dry_run=dry)
if r["added"]:
results.append(r)
total += r["added"]
print(f"Files: {len(results)} Insertions: {total}")
if dry:
print("Run with --apply.")
if __name__ == "__main__":
main()
# #endregion AddDefgroupIngroup

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region ApiRoutesModule [C:5] [TYPE Module] [SEMANTICS api, package, router, lazy, import] # #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. # @BRIEF Provide lazy route module loading to avoid heavyweight imports during tests.
# @LAYER API # @LAYER API
# @RELATION CALLS -> [ApiRoutesGetAttr] # @RELATION CALLS -> [ApiRoutesGetAttr]
@@ -8,6 +9,7 @@
# @INVARIANT Only names listed in __all__ are importable via __getattr__. # @INVARIANT Only names listed in __all__ are importable via __getattr__.
# #region Route_Group_Contracts [C:3] [TYPE Block] # #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. # @BRIEF Declare the canonical route-module registry used by lazy imports and app router inclusion.
# @RELATION DEPENDS_ON -> [PluginsRouter] # @RELATION DEPENDS_ON -> [PluginsRouter]
# @RELATION DEPENDS_ON -> [TasksRouter] # @RELATION DEPENDS_ON -> [TasksRouter]
@@ -45,6 +47,7 @@ __all__ = [
# #region ApiRoutesGetAttr [C:3] [TYPE Function] # #region ApiRoutesGetAttr [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lazily import route module by attribute name. # @BRIEF Lazily import route module by attribute name.
# @RELATION DEPENDS_ON -> [ApiRoutesModule] # @RELATION DEPENDS_ON -> [ApiRoutesModule]
# @PRE name is module candidate exposed in __all__. # @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] # #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. # @BRIEF Admin API endpoints for user and role management.
# @LAYER API # @LAYER API
@@ -35,6 +36,7 @@ from ...services.rbac_permission_catalog import (
) )
# #region router [TYPE Variable] # #region router [TYPE Variable]
# @ingroup Api
# @RELATION DEPENDS_ON -> fastapi.APIRouter # @RELATION DEPENDS_ON -> fastapi.APIRouter
# @BRIEF APIRouter instance for admin routes. # @BRIEF APIRouter instance for admin routes.
router = APIRouter(prefix="/api/admin", tags=["admin"]) 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] # #region list_users [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all registered users. # @BRIEF Lists all registered users.
# @PRE Current user has 'Admin' role. # @PRE Current user has 'Admin' role.
# @POST Returns a list of UserSchema objects. # @POST Returns a list of UserSchema objects.
@@ -59,6 +62,7 @@ async def list_users(
# #region create_user [C:3] [TYPE Function] # #region create_user [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Creates a new local user. # @BRIEF Creates a new local user.
# @PRE Current user has 'Admin' role. # @PRE Current user has 'Admin' role.
# @POST New user is created in the database. # @POST New user is created in the database.
@@ -97,6 +101,7 @@ async def create_user(
# #region update_user [C:3] [TYPE Function] # #region update_user [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing user. # @BRIEF Updates an existing user.
# @PRE Current user has 'Admin' role. # @PRE Current user has 'Admin' role.
# @POST User record is updated in the database. # @POST User record is updated in the database.
@@ -137,6 +142,7 @@ async def update_user(
# #region delete_user [C:3] [TYPE Function] # #region delete_user [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Deletes a user. # @BRIEF Deletes a user.
# @PRE Current user has 'Admin' role. # @PRE Current user has 'Admin' role.
# @POST User record is removed from the database. # @POST User record is removed from the database.
@@ -174,6 +180,7 @@ async def delete_user(
# #region list_roles [C:3] [TYPE Function] # #region list_roles [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all available roles. # @BRIEF Lists all available roles.
# @RELATION CALLS -> [Role] # @RELATION CALLS -> [Role]
@router.get("/roles", response_model=list[RoleSchema]) @router.get("/roles", response_model=list[RoleSchema])
@@ -188,6 +195,7 @@ async def list_roles(
# #region create_role [C:3] [TYPE Function] # #region create_role [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Creates a new system role with associated permissions. # @BRIEF Creates a new system role with associated permissions.
# @PRE Role name must be unique. # @PRE Role name must be unique.
# @POST New Role record is created in auth.db. # @POST New Role record is created in auth.db.
@@ -225,6 +233,7 @@ async def create_role(
# #region update_role [C:3] [TYPE Function] # #region update_role [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing role's metadata and permissions. # @BRIEF Updates an existing role's metadata and permissions.
# @PRE role_id must be a valid existing role UUID. # @PRE role_id must be a valid existing role UUID.
# @POST Role record is updated in auth.db. # @POST Role record is updated in auth.db.
@@ -268,6 +277,7 @@ async def update_role(
# #region delete_role [C:3] [TYPE Function] # #region delete_role [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Removes a role from the system. # @BRIEF Removes a role from the system.
# @PRE role_id must be a valid existing role UUID. # @PRE role_id must be a valid existing role UUID.
# @POST Role record is removed from auth.db. # @POST Role record is removed from auth.db.
@@ -294,6 +304,7 @@ async def delete_role(
# #region list_permissions [C:3] [TYPE Function] # #region list_permissions [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all available system permissions for assignment. # @BRIEF Lists all available system permissions for assignment.
# @POST Returns a list of all PermissionSchema objects. # @POST Returns a list of all PermissionSchema objects.
# @RELATION CALLS -> backend.src.core.auth.repository.AuthRepository.list_permissions # @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] # #region list_ad_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all AD Group to Role mappings. # @BRIEF Lists all AD Group to Role mappings.
# @RELATION CALLS -> ADGroupMapping # @RELATION CALLS -> ADGroupMapping
@router.get("/ad-mappings", response_model=list[ADGroupMappingSchema]) @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] # #region create_ad_mapping [C:2] [TYPE Function]
# @ingroup Api
# @RELATION DEPENDS_ON -> [ADGroupMapping] # @RELATION DEPENDS_ON -> [ADGroupMapping]
# @RELATION DEPENDS_ON -> [get_auth_db] # @RELATION DEPENDS_ON -> [get_auth_db]
# @RELATION DEPENDS_ON -> [has_permission] # @RELATION DEPENDS_ON -> [has_permission]

View File

@@ -1,4 +1,5 @@
# #region AdminApiKeyRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, admin, api_key, crud] # #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. # @BRIEF Admin API endpoints for API key management — list, generate (one-time reveal), and revoke.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [APIKeyModel] # @RELATION DEPENDS_ON -> [APIKeyModel]
@@ -20,6 +21,7 @@ from ...dependencies import has_permission
from ...models.api_key import APIKey from ...models.api_key import APIKey
# #region router [TYPE Variable] # #region router [TYPE Variable]
# @ingroup Api
# @BRIEF APIRouter for admin API key management routes. # @BRIEF APIRouter for admin API key management routes.
router = APIRouter(prefix="/api/admin/api-keys", tags=["admin", "api-keys"]) router = APIRouter(prefix="/api/admin/api-keys", tags=["admin", "api-keys"])
# #endregion router # #endregion router
@@ -76,6 +78,7 @@ class ApiKeyRevokeResponse(BaseModel):
# ── Routes ──────────────────────────────────────────────────── # ── Routes ────────────────────────────────────────────────────
# #region list_api_keys [C:2] [TYPE Function] # #region list_api_keys [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF List all API keys — NEVER returns key_hash or raw_key. # @BRIEF List all API keys — NEVER returns key_hash or raw_key.
# @PRE Requires admin:settings WRITE permission. # @PRE Requires admin:settings WRITE permission.
# @POST Returns list of ApiKeyListItem without sensitive fields. # @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] # #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. # @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. # @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. # @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] # #region revoke_api_key [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Revoke an API key by setting active=False. Preserves row for audit. # @BRIEF Revoke an API key by setting active=False. Preserves row for audit.
# @PRE Requires admin:settings WRITE permission. # @PRE Requires admin:settings WRITE permission.
# @POST Sets active=False on the key. Returns 404 if already revoked or not found. # @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] # #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. # @BRIEF API routes for LLM assistant command parsing and safe execution orchestration.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [TaskManager] # @RELATION DEPENDS_ON -> [TaskManager]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region AssistantToolBackup [C:3] [TYPE Module] [SEMANTICS assistant, tool, backup] # #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. # @BRIEF Handler for the "run_backup" tool — run backup for environment or specific dashboard.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @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] # #region handle_run_backup [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="run_backup", operation="run_backup",
domain="backup", domain="backup",

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region AssistantToolDeploy [C:3] [TYPE Module] [SEMANTICS assistant, tool, git, deploy] # #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. # @BRIEF Handler for the "deploy_dashboard" tool — deploy dashboard to target environment.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @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] # #region handle_deploy_dashboard [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="deploy_dashboard", operation="deploy_dashboard",
domain="git", domain="git",

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region AssistantToolLlmDocumentation [C:3] [TYPE Module] [SEMANTICS assistant, tool, llm, documentation] # #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. # @BRIEF Handler for the "run_llm_documentation" tool — generate dataset documentation via LLM.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @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] # #region handle_run_llm_documentation [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="run_llm_documentation", operation="run_llm_documentation",
domain="llm", domain="llm",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolLlmValidation [C:3] [TYPE Module] [SEMANTICS assistant, tool, llm, validation, task] # #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. # @BRIEF Handler for the "run_llm_validation" tool — create a validation task (policy) and trigger a run.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @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] # #region handle_run_llm_validation [C:4] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="run_llm_validation", operation="run_llm_validation",
domain="llm", domain="llm",

View File

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

View File

@@ -1,4 +1,5 @@
# #region AssistantToolMigration [C:4] [TYPE Module] [SEMANTICS assistant, tool, migration, execute] # #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. # @BRIEF Handler for the "execute_migration" tool — run dashboard migration between environments.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @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] # #region handle_execute_migration [C:4] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="execute_migration", operation="execute_migration",
domain="migration", domain="migration",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolRegistry [C:4] [TYPE Module] [SEMANTICS assistant, registry, tool, dispatch, catalog] # #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, # @BRIEF Central `@assistant_tool` decorator-based registry: auto-generates LLM catalog, dispatches operations,
# and enforces permissions. Replaces hand-maintained `_build_tool_catalog()`, `_dispatch_intent()`, # and enforces permissions. Replaces hand-maintained `_build_tool_catalog()`, `_dispatch_intent()`,
# `INTENT_PERMISSION_CHECKS`, and `_SAFE_OPS` to eliminate cross-file desynchronisation. # `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] # #region assistant_tool [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Decorator that registers a function as an assistant tool handler. # @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. # @POST Tool is registered in `_tools` by operation name. Handler gets `__assistant_tool__` attr.
# @SIDE_EFFECT Mutates the module-level `_tools` dict. # @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] # #region get_catalog [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Build LLM tool catalog from registry, filtered by user permissions. # @BRIEF Build LLM tool catalog from registry, filtered by user permissions.
# @POST Returns list of dicts suitable for LLM planner prompt. # @POST Returns list of dicts suitable for LLM planner prompt.
def get_catalog( def get_catalog(
@@ -232,6 +235,7 @@ def get_catalog(
# #region dispatch [C:2] [TYPE Function] # #region dispatch [C:2] [TYPE Function]
# @ingroup AssistantApi
# @BRIEF Look up operation in registry and call its handler. # @BRIEF Look up operation in registry and call its handler.
# @PRE operation is a known registered tool. # @PRE operation is a known registered tool.
# @POST Returns (text, task_id, actions) from the handler. # @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] # #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. # @BRIEF Handler for the "search_dashboards" tool — search and list dashboards from an environment.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @RELATION DEPENDS_ON -> [AssistantToolRegistry]
@@ -22,6 +23,7 @@ from ._tool_registry import assistant_tool
# #region handle_search_dashboards [C:3] [TYPE Function] # #region handle_search_dashboards [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="search_dashboards", operation="search_dashboards",
domain="dashboards", domain="dashboards",

View File

@@ -1,4 +1,5 @@
# #region AssistantToolTaskStatus [C:3] [TYPE Module] [SEMANTICS assistant, tool, status, task] # #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. # @BRIEF Handler for the "get_task_status" tool — retrieve task status by id or latest user task.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry] # @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] # #region handle_get_task_status [C:3] [TYPE Function]
# @ingroup AssistantApi
@assistant_tool( @assistant_tool(
operation="get_task_status", operation="get_task_status",
domain="status", domain="status",

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region DashboardActionRoutes [C:2] [TYPE Module] [SEMANTICS fastapi, dashboard, api, backup] # #region DashboardActionRoutes [C:2] [TYPE Module] [SEMANTICS fastapi, dashboard, api, backup]
# @defgroup Api Module group.
# @BRIEF Dashboard action route handlers — migrate, backup. # @BRIEF Dashboard action route handlers — migrate, backup.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]] # @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]]
@@ -22,6 +23,7 @@ from ._schemas import (
# #region migrate_dashboards [C:2] [TYPE Function] # #region migrate_dashboards [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk migration of dashboards from source to target environment # @BRIEF Trigger bulk migration of dashboards from source to target environment
# @PRE User has permission plugin:migration:execute # @PRE User has permission plugin:migration:execute
# @PRE source_env_id and target_env_id are valid environment IDs # @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] # #region backup_dashboards [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk backup of dashboards with optional cron schedule # @BRIEF Trigger bulk backup of dashboards with optional cron schedule
# @PRE User has permission plugin:backup:execute # @PRE User has permission plugin:backup:execute
# @PRE env_id is a valid environment ID # @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] # #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. # @BRIEF Dashboard detail, db-mappings, task history, thumbnail route handlers.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]] # @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]]
@@ -41,6 +42,7 @@ from ._schemas import (
# #region get_database_mappings [C:2] [TYPE Function] # #region get_database_mappings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get database mapping suggestions between source and target environments # @BRIEF Get database mapping suggestions between source and target environments
# @PRE User has permission plugin:migration:read # @PRE User has permission plugin:migration:read
# @PRE source_env_id and target_env_id are valid environment IDs # @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] # #region get_dashboard_detail [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch detailed dashboard info with related charts and datasets # @BRIEF Fetch detailed dashboard info with related charts and datasets
# @PRE env_id must be valid and dashboard ref (slug or id) must exist # @PRE env_id must be valid and dashboard ref (slug or id) must exist
# @POST Returns dashboard detail payload for overview page # @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] # #region get_dashboard_tasks_history [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Returns history of backup and LLM validation tasks for a dashboard. # @BRIEF Returns history of backup and LLM validation tasks for a dashboard.
# @PRE dashboard ref (slug or id) is valid. # @PRE dashboard ref (slug or id) is valid.
# @POST Response contains sorted task history (newest first). # @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] # #region get_dashboard_thumbnail [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Proxies Superset dashboard thumbnail with cache support. # @BRIEF Proxies Superset dashboard thumbnail with cache support.
# @RELATION CALLS -> [AsyncAPIClient] # @RELATION CALLS -> [AsyncAPIClient]
# @PRE env_id must exist. # @PRE env_id must exist.

View File

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

View File

@@ -1,4 +1,5 @@
# #region DashboardListingRoutes [C:4] [TYPE Module] [SEMANTICS fastapi, dashboard, api, search] # #region DashboardListingRoutes [C:4] [TYPE Module] [SEMANTICS fastapi, dashboard, api, search]
# @defgroup Api Module group.
# @BRIEF Dashboard listing route handler for Dashboard Hub. # @BRIEF Dashboard listing route handler for Dashboard Hub.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]] # @RELATION DEPENDS_ON -> [EXT:frontend:DashboardRouter]]
@@ -35,6 +36,7 @@ from ._schemas import DashboardsResponse, EffectiveProfileFilter
# #region get_dashboards [C:3] [TYPE Function] # #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 # @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 env_id must be a valid environment ID
# @PRE page must be >= 1 if provided # @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] # #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. # @BRIEF Dashboard response projection and profile-filter helpers for Dashboard Hub routes.
# @LAYER Infrastructure # @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [SupersetClient] # @RELATION DEPENDS_ON -> [SupersetClient]

View File

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

View File

@@ -1,4 +1,5 @@
# #region DatasetReviewApi [C:3] [TYPE Module] [SEMANTICS dataset, review, api, facade] # #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. # @BRIEF Thin facade re-exporting router and public symbols from decomposed dataset review API sub-modules.
# @LAYER API # @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). # @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] # #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. # @BRIEF Dependency injection, serialization helpers, and feature-flag guards for dataset review endpoints.
# @LAYER API # @LAYER API

View File

@@ -1,4 +1,5 @@
# #region DatasetReviewRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, dataset, review, feedback, route] # #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. # @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] # #region list_sessions [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List resumable dataset review sessions for the current user. # @BRIEF List resumable dataset review sessions for the current user.
@router.get( @router.get(
"/sessions", "/sessions",
@@ -137,6 +139,7 @@ async def list_sessions(
# #region start_session [C:4] [TYPE Function] # #region start_session [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Start a new dataset review session from a Superset link or dataset selection. # @BRIEF Start a new dataset review session from a Superset link or dataset selection.
@router.post( @router.post(
"/sessions", "/sessions",
@@ -191,6 +194,7 @@ async def start_session(
# #region get_session_detail [C:3] [TYPE Function] # #region get_session_detail [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Return the full accessible dataset review session aggregate. # @BRIEF Return the full accessible dataset review session aggregate.
@router.get( @router.get(
"/sessions/{session_id}", "/sessions/{session_id}",
@@ -214,6 +218,7 @@ async def get_session_detail(
# #region update_session [C:3] [TYPE Function] # #region update_session [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Update resumable lifecycle status for an owned session. # @BRIEF Update resumable lifecycle status for an owned session.
@router.patch( @router.patch(
"/sessions/{session_id}", "/sessions/{session_id}",
@@ -263,6 +268,7 @@ async def update_session(
# #region delete_session [C:3] [TYPE Function] # #region delete_session [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Archive or hard-delete a session owned by the current user. # @BRIEF Archive or hard-delete a session owned by the current user.
@router.delete( @router.delete(
"/sessions/{session_id}", "/sessions/{session_id}",
@@ -314,6 +320,7 @@ async def delete_session(
# #region export_documentation [C:3] [TYPE Function] # #region export_documentation [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Export documentation output for the current session. # @BRIEF Export documentation output for the current session.
@router.get( @router.get(
"/sessions/{session_id}/exports/documentation", "/sessions/{session_id}/exports/documentation",
@@ -352,6 +359,7 @@ async def export_documentation(
# #region export_validation [C:3] [TYPE Function] # #region export_validation [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Export validation findings for the current session. # @BRIEF Export validation findings for the current session.
@router.get( @router.get(
"/sessions/{session_id}/exports/validation", "/sessions/{session_id}/exports/validation",
@@ -390,6 +398,7 @@ async def export_validation(
# #region get_clarification_state [C:3] [TYPE Function] # #region get_clarification_state [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Return the current clarification session summary and active question payload. # @BRIEF Return the current clarification session summary and active question payload.
@router.get( @router.get(
"/sessions/{session_id}/clarification", "/sessions/{session_id}/clarification",
@@ -426,6 +435,7 @@ async def get_clarification_state(
# #region resume_clarification [C:3] [TYPE Function] # #region resume_clarification [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Resume clarification mode on the highest-priority unresolved question. # @BRIEF Resume clarification mode on the highest-priority unresolved question.
@router.post( @router.post(
"/sessions/{session_id}/clarification/resume", "/sessions/{session_id}/clarification/resume",
@@ -463,6 +473,7 @@ async def resume_clarification(
# #region record_clarification_answer [C:3] [TYPE Function] # #region record_clarification_answer [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist one clarification answer before advancing the active pointer. # @BRIEF Persist one clarification answer before advancing the active pointer.
@router.post( @router.post(
"/sessions/{session_id}/clarification/answers", "/sessions/{session_id}/clarification/answers",
@@ -513,6 +524,7 @@ async def record_clarification_answer(
# #region update_field_semantic [C:3] [TYPE Function] # #region update_field_semantic [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Apply one field-level semantic candidate decision or manual override. # @BRIEF Apply one field-level semantic candidate decision or manual override.
@router.patch( @router.patch(
"/sessions/{session_id}/fields/{field_id}/semantic", "/sessions/{session_id}/fields/{field_id}/semantic",
@@ -553,6 +565,7 @@ async def update_field_semantic(
# #region lock_field_semantic [C:3] [TYPE Function] # #region lock_field_semantic [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Lock one semantic field against later automatic overwrite. # @BRIEF Lock one semantic field against later automatic overwrite.
@router.post( @router.post(
"/sessions/{session_id}/fields/{field_id}/lock", "/sessions/{session_id}/fields/{field_id}/lock",
@@ -593,6 +606,7 @@ async def lock_field_semantic(
# #region unlock_field_semantic [C:3] [TYPE Function] # #region unlock_field_semantic [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Unlock one semantic field so later automated candidate application may replace it. # @BRIEF Unlock one semantic field so later automated candidate application may replace it.
@router.post( @router.post(
"/sessions/{session_id}/fields/{field_id}/unlock", "/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] # #region approve_batch_semantic_fields [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Approve multiple semantic candidate decisions in one batch. # @BRIEF Approve multiple semantic candidate decisions in one batch.
@router.post( @router.post(
"/sessions/{session_id}/fields/semantic/approve-batch", "/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] # #region list_execution_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Return the current mapping-review set for one accessible session. # @BRIEF Return the current mapping-review set for one accessible session.
@router.get( @router.get(
"/sessions/{session_id}/mappings", "/sessions/{session_id}/mappings",
@@ -712,6 +728,7 @@ async def list_execution_mappings(
# #region update_execution_mapping [C:3] [TYPE Function] # #region update_execution_mapping [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist one owner-authorized execution-mapping effective value override. # @BRIEF Persist one owner-authorized execution-mapping effective value override.
@router.patch( @router.patch(
"/sessions/{session_id}/mappings/{mapping_id}", "/sessions/{session_id}/mappings/{mapping_id}",
@@ -777,6 +794,7 @@ async def update_execution_mapping(
# #region approve_execution_mapping [C:3] [TYPE Function] # #region approve_execution_mapping [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Explicitly approve a warning-sensitive mapping transformation. # @BRIEF Explicitly approve a warning-sensitive mapping transformation.
@router.post( @router.post(
"/sessions/{session_id}/mappings/{mapping_id}/approve", "/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] # #region approve_batch_execution_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Approve multiple warning-sensitive execution mappings in one batch. # @BRIEF Approve multiple warning-sensitive execution mappings in one batch.
@router.post( @router.post(
"/sessions/{session_id}/mappings/approve-batch", "/sessions/{session_id}/mappings/approve-batch",
@@ -877,6 +896,7 @@ async def approve_batch_execution_mappings(
# #region trigger_preview_generation [C:3] [TYPE Function] # #region trigger_preview_generation [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger Superset-side preview compilation for the current owned execution context. # @BRIEF Trigger Superset-side preview compilation for the current owned execution context.
@router.post( @router.post(
"/sessions/{session_id}/preview", "/sessions/{session_id}/preview",
@@ -938,6 +958,7 @@ async def trigger_preview_generation(
# #region launch_dataset [C:3] [TYPE Function] # #region launch_dataset [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Execute the current owned session launch handoff and return audited SQL Lab run context. # @BRIEF Execute the current owned session launch handoff and return audited SQL Lab run context.
@router.post( @router.post(
"/sessions/{session_id}/launch", "/sessions/{session_id}/launch",
@@ -996,6 +1017,7 @@ async def launch_dataset(
# #region record_field_feedback [C:3] [TYPE Function] # #region record_field_feedback [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist thumbs up/down feedback for AI-assisted semantic field content. # @BRIEF Persist thumbs up/down feedback for AI-assisted semantic field content.
@router.post( @router.post(
"/sessions/{session_id}/fields/{field_id}/feedback", "/sessions/{session_id}/fields/{field_id}/feedback",
@@ -1040,6 +1062,7 @@ async def record_field_feedback(
# #region record_clarification_feedback [C:3] [TYPE Function] # #region record_clarification_feedback [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Persist thumbs up/down feedback for clarification question/answer content. # @BRIEF Persist thumbs up/down feedback for clarification question/answer content.
@router.post( @router.post(
"/sessions/{session_id}/clarification/questions/{question_id}/feedback", "/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] # #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 # @BRIEF API endpoints for the Dataset Hub - listing datasets with mapping progress
# @LAYER API # @LAYER API
@@ -84,6 +85,7 @@ class MetricItem(BaseModel):
# #endregion MetricItem # #endregion MetricItem
# #region DatasetDetailResponse [C:2] [TYPE DataClass] # #region DatasetDetailResponse [C:2] [TYPE DataClass]
# @ingroup Api
# @BRIEF Detailed DTO for a dataset including columns, linked dashboards, and metrics. # @BRIEF Detailed DTO for a dataset including columns, linked dashboards, and metrics.
# @RELATION DEPENDS_ON -> [MetricItem] # @RELATION DEPENDS_ON -> [MetricItem]
class DatasetDetailResponse(BaseModel): class DatasetDetailResponse(BaseModel):
@@ -116,6 +118,7 @@ class StatsCounts(BaseModel):
# #endregion StatsCounts # #endregion StatsCounts
# #region DatasetsResponse [C:2] [TYPE DataClass] # #region DatasetsResponse [C:2] [TYPE DataClass]
# @ingroup Api
# @BRIEF Paginated response DTO for dataset listings with stats. # @BRIEF Paginated response DTO for dataset listings with stats.
# @RELATION DEPENDS_ON -> [StatsCounts] # @RELATION DEPENDS_ON -> [StatsCounts]
class DatasetsResponse(BaseModel): class DatasetsResponse(BaseModel):
@@ -146,6 +149,7 @@ class MetricDescriptionUpdate(BaseModel):
# #endregion MetricDescriptionUpdate # #endregion MetricDescriptionUpdate
# #region get_dataset_ids [C:4] [TYPE Function] # #region get_dataset_ids [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch list of all dataset IDs from a specific environment (without pagination) # @BRIEF Fetch list of all dataset IDs from a specific environment (without pagination)
# @PRE env_id must be a valid environment ID # @PRE env_id must be a valid environment ID
# @POST Returns a list of all dataset IDs # @POST Returns a list of all dataset IDs
@@ -194,6 +198,7 @@ async def get_dataset_ids(
# #endregion get_dataset_ids # #endregion get_dataset_ids
# #region get_datasets [C:4] [TYPE Function] # #region get_datasets [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch list of datasets from a specific environment with mapping progress and stats. # @BRIEF Fetch list of datasets from a specific environment with mapping progress and stats.
# @PRE env_id must be a valid environment ID # @PRE env_id must be a valid environment ID
# @PRE page must be >= 1 if provided # @PRE page must be >= 1 if provided
@@ -321,6 +326,7 @@ class MapColumnsRequest(BaseModel):
# #endregion MapColumnsRequest # #endregion MapColumnsRequest
# #region map_columns [C:4] [TYPE Function] # #region map_columns [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk column mapping for datasets # @BRIEF Trigger bulk column mapping for datasets
# @PRE User has permission plugin:mapper:execute # @PRE User has permission plugin:mapper:execute
# @PRE env_id is a valid environment ID # @PRE env_id is a valid environment ID
@@ -394,6 +400,7 @@ class GenerateDocsRequest(BaseModel):
# #endregion GenerateDocsRequest # #endregion GenerateDocsRequest
# #region generate_docs [C:4] [TYPE Function] # #region generate_docs [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger bulk documentation generation for datasets # @BRIEF Trigger bulk documentation generation for datasets
# @PRE User has permission plugin:llm_analysis:execute # @PRE User has permission plugin:llm_analysis:execute
# @PRE env_id is a valid environment ID # @PRE env_id is a valid environment ID
@@ -447,6 +454,7 @@ async def generate_docs(
# #endregion generate_docs # #endregion generate_docs
# #region get_dataset_detail [C:4] [TYPE Function] # #region get_dataset_detail [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get detailed dataset information including columns and linked dashboards # @BRIEF Get detailed dataset information including columns and linked dashboards
# @PRE env_id is a valid environment ID # @PRE env_id is a valid environment ID
# @PRE dataset_id is a valid dataset ID # @PRE dataset_id is a valid dataset ID
@@ -502,6 +510,7 @@ def _strip_html_tags(text: str) -> str:
# #endregion _strip_html_tags # #endregion _strip_html_tags
# #region update_column_description [C:4] [TYPE Endpoint] # #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. # @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. # @PRE dataset_id and column_id must exist in the target environment.
# @POST Column description in Superset is updated. Response confirms success. # @POST Column description in Superset is updated. Response confirms success.
@@ -591,6 +600,7 @@ async def update_column_description(
# #endregion update_column_description # #endregion update_column_description
# #region update_metric_description [C:4] [TYPE Endpoint] # #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. # @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. # @PRE dataset_id and metric_id must exist in the target environment.
# @POST Metric description in Superset is updated. # @POST Metric description in Superset is updated.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region GitMergeRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, git, api] # #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). # @BRIEF FastAPI endpoints for merge operations (status, conflicts, resolve, abort, continue).
# @LAYER API # @LAYER API
@@ -22,6 +23,7 @@ from ._router import router
# #region get_merge_status [C:2] [TYPE Function] # #region get_merge_status [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return unfinished-merge status for repository (web-only recovery support). # @BRIEF Return unfinished-merge status for repository (web-only recovery support).
@router.get( @router.get(
"/repositories/{dashboard_ref}/merge/status", response_model=MergeStatusSchema "/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] # #region get_merge_conflicts [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return conflicted files with mine/theirs previews for web conflict resolver. # @BRIEF Return conflicted files with mine/theirs previews for web conflict resolver.
@router.get( @router.get(
"/repositories/{dashboard_ref}/merge/conflicts", "/repositories/{dashboard_ref}/merge/conflicts",
@@ -77,6 +80,7 @@ async def get_merge_conflicts(
# #region resolve_merge_conflicts [C:3] [TYPE Function] # #region resolve_merge_conflicts [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Apply mine/theirs/manual conflict resolutions from WebUI and stage files. # @BRIEF Apply mine/theirs/manual conflict resolutions from WebUI and stage files.
# @RELATION CALLS -> [GitService] # @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/merge/resolve") @router.post("/repositories/{dashboard_ref}/merge/resolve")
@@ -108,6 +112,7 @@ async def resolve_merge_conflicts(
# #region abort_merge [C:2] [TYPE Function] # #region abort_merge [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Abort unfinished merge from WebUI flow. # @BRIEF Abort unfinished merge from WebUI flow.
@router.post("/repositories/{dashboard_ref}/merge/abort") @router.post("/repositories/{dashboard_ref}/merge/abort")
async def abort_merge( async def abort_merge(
@@ -133,6 +138,7 @@ async def abort_merge(
# #region continue_merge [C:3] [TYPE Function] # #region continue_merge [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Finalize unfinished merge from WebUI flow. # @BRIEF Finalize unfinished merge from WebUI flow.
# @RELATION CALLS -> [GitService] # @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/merge/continue") @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] # #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). # @BRIEF FastAPI endpoints for Git lifecycle operations (sync, promote, deploy).
# @LAYER API # @LAYER API
@@ -27,6 +28,7 @@ from ._router import router
# #region sync_dashboard [C:3] [TYPE Function] # #region sync_dashboard [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Sync dashboard state from Superset to Git using the GitPlugin. # @BRIEF Sync dashboard state from Superset to Git using the GitPlugin.
# @RELATION CALLS -> [GitPlugin] # @RELATION CALLS -> [GitPlugin]
@router.post("/repositories/{dashboard_ref}/sync") @router.post("/repositories/{dashboard_ref}/sync")
@@ -62,6 +64,7 @@ async def sync_dashboard(
# #region promote_dashboard [C:3] [TYPE Function] # #region promote_dashboard [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Promote changes between branches via MR or direct merge. # @BRIEF Promote changes between branches via MR or direct merge.
# @RELATION CALLS -> [GitPlugin] # @RELATION CALLS -> [GitPlugin]
@router.post("/repositories/{dashboard_ref}/promote", response_model=PromoteResponse) @router.post("/repositories/{dashboard_ref}/promote", response_model=PromoteResponse)
@@ -185,6 +188,7 @@ async def promote_dashboard(
# #region deploy_dashboard [C:3] [TYPE Function] # #region deploy_dashboard [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Deploy dashboard from Git to a target environment. # @BRIEF Deploy dashboard from Git to a target environment.
# @RELATION CALLS -> [GitPlugin] # @RELATION CALLS -> [GitPlugin]
@router.post("/repositories/{dashboard_ref}/deploy") @router.post("/repositories/{dashboard_ref}/deploy")

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region health_router [C:3] [TYPE Module] [SEMANTICS fastapi, health, api, search, dashboard] # #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. # @BRIEF API endpoints for dashboard health monitoring and status aggregation.
# @LAYER UI # @LAYER UI
# @RELATION DEPENDS_ON -> [health_service] # @RELATION DEPENDS_ON -> [health_service]
@@ -15,6 +16,7 @@ from ...services.health_service import HealthService
router = APIRouter(prefix="/api/health", tags=["Health"]) router = APIRouter(prefix="/api/health", tags=["Health"])
# #region get_health_summary [TYPE Function] # #region get_health_summary [TYPE Function]
# @ingroup Api
# @BRIEF Get aggregated health status for all dashboards. # @BRIEF Get aggregated health status for all dashboards.
# @PRE Caller has read permission for dashboard health view. # @PRE Caller has read permission for dashboard health view.
# @POST Returns HealthSummaryResponse. # @POST Returns HealthSummaryResponse.
@@ -38,6 +40,7 @@ async def get_health_summary(
# #region delete_health_report [TYPE Function] # #region delete_health_report [TYPE Function]
# @ingroup Api
# @BRIEF Delete one persisted dashboard validation report from health summary. # @BRIEF Delete one persisted dashboard validation report from health summary.
# @PRE Caller has write permission for tasks/report maintenance. # @PRE Caller has write permission for tasks/report maintenance.
# @POST Validation record is removed; linked task/logs are cleaned when available. # @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] # #region LlmRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, llm, api, provider]
# @defgroup Api Module group.
# @BRIEF API routes for LLM provider configuration and management. # @BRIEF API routes for LLM provider configuration and management.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [LLMProviderService] # @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] # #region get_providers [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve all LLM provider configurations. # @BRIEF Retrieve all LLM provider configurations.
# @PRE User is authenticated. # @PRE User is authenticated.
# @POST Returns list of LLMProviderConfig. # @POST Returns list of LLMProviderConfig.
@@ -96,6 +98,7 @@ async def get_providers(
# #region fetch_models [C:4] [TYPE Function] # #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. # @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. # @PRE User is authenticated. Either provider_id or base_url+provider_type must be provided.
# @POST Returns a list of available model IDs. # @POST Returns a list of available model IDs.
@@ -175,6 +178,7 @@ async def fetch_models(
# #region get_llm_status [C:4] [TYPE Function] # #region get_llm_status [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Returns whether LLM runtime is configured for dashboard validation. # @BRIEF Returns whether LLM runtime is configured for dashboard validation.
# @PRE User is authenticated. # @PRE User is authenticated.
# @POST configured=true only when an active provider with valid decrypted key exists. # @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] # #region create_provider [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new LLM provider configuration. # @BRIEF Create a new LLM provider configuration.
# @PRE User is authenticated and has admin permissions. # @PRE User is authenticated and has admin permissions.
# @POST Returns the created LLMProviderConfig. # @POST Returns the created LLMProviderConfig.
@@ -283,6 +288,7 @@ async def create_provider(
# #region update_provider [C:4] [TYPE Function] # #region update_provider [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing LLM provider configuration. # @BRIEF Update an existing LLM provider configuration.
# @PRE User is authenticated and has admin permissions. # @PRE User is authenticated and has admin permissions.
# @POST Returns the updated LLMProviderConfig. # @POST Returns the updated LLMProviderConfig.
@@ -322,6 +328,7 @@ async def update_provider(
# #region delete_provider [C:4] [TYPE Function] # #region delete_provider [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete an LLM provider configuration. # @BRIEF Delete an LLM provider configuration.
# @PRE User is authenticated and has admin permissions. # @PRE User is authenticated and has admin permissions.
# @POST Returns success status. # @POST Returns success status.
@@ -360,6 +367,7 @@ async def delete_provider(
# #region test_connection [C:4] [TYPE Function] # #region test_connection [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Test connection to an LLM provider. # @BRIEF Test connection to an LLM provider.
# @PRE User is authenticated. # @PRE User is authenticated.
# @POST Returns success status and message. # @POST Returns success status and message.
@@ -415,6 +423,7 @@ async def test_connection(
# #region test_provider_config [C:4] [TYPE Function] # #region test_provider_config [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Test connection with a provided configuration (not yet saved). # @BRIEF Test connection with a provided configuration (not yet saved).
# @PRE User is authenticated. # @PRE User is authenticated.
# @POST Returns success status and message. # @POST Returns success status and message.
@@ -466,6 +475,7 @@ class ProbeMaxImagesResponse(BaseModel):
# #region probe_max_images [C:4] [TYPE Function] # #region probe_max_images [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Probe an LLM provider to discover its max images per request limit. # @BRIEF Probe an LLM provider to discover its max images per request limit.
# @PRE Provider exists and has valid API key. # @PRE Provider exists and has valid API key.
# @POST Returns the detected max_images limit and caches it on the provider. # @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] # #region MaintenanceRouter [C:3] [TYPE Module] [SEMANTICS fastapi, router, maintenance, api]
# @defgroup Api Module group.
# @BRIEF FastAPI APIRouter for the Maintenance Banner endpoints. # @BRIEF FastAPI APIRouter for the Maintenance Banner endpoints.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [MaintenanceRoutesModule] # @RELATION DEPENDS_ON -> [MaintenanceRoutesModule]

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# #region MigrationApi [C:5] [TYPE Module] [SEMANTICS fastapi, migration, api, sync, search, mapping] # #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. # @BRIEF HTTP contract layer for migration orchestration, settings, dry-run, and mapping sync endpoints.
# @LAYER Infrastructure # @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [AppDependencies] # @RELATION DEPENDS_ON -> [AppDependencies]
@@ -41,6 +42,7 @@ router = APIRouter(prefix="/api", tags=["migration"])
# #region get_dashboards [C:3] [TYPE Function] # #region get_dashboards [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch dashboard metadata from a requested environment for migration selection UI. # @BRIEF Fetch dashboard metadata from a requested environment for migration selection UI.
# @PRE env_id is provided and exists in configured environments. # @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. # @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] # #region execute_migration [C:5] [TYPE Function]
# @ingroup Api
# @BRIEF Validate migration selection and enqueue asynchronous migration task execution. # @BRIEF Validate migration selection and enqueue asynchronous migration task execution.
# @PRE DashboardSelection payload is valid and both source/target environments exist. # @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. # @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] # #region dry_run_migration [C:5] [TYPE Function]
# @ingroup Api
# @BRIEF Build pre-flight migration diff and risk summary without mutating target systems. # @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. # @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. # @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] # #region get_migration_settings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Read and return configured migration synchronization cron expression. # @BRIEF Read and return configured migration synchronization cron expression.
# @PRE Configuration store is available and requester has READ permission. # @PRE Configuration store is available and requester has READ permission.
# @POST Returns {"cron": str} reflecting current persisted settings value. # @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] # #region update_migration_settings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Validate and persist migration synchronization cron expression update. # @BRIEF Validate and persist migration synchronization cron expression update.
# @PRE Payload includes "cron" key and requester has WRITE permission. # @PRE Payload includes "cron" key and requester has WRITE permission.
# @POST Returns {"cron": str, "status": "updated"} and persists updated cron value. # @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] # #region get_resource_mappings [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Fetch synchronized resource mappings with optional filters and pagination for migration mappings view. # @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. # @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. # @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] # #region trigger_sync_now [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger immediate ID synchronization for every configured environment. # @BRIEF Trigger immediate ID synchronization for every configured environment.
# @PRE At least one environment is configured and requester has EXECUTE permission. # @PRE At least one environment is configured and requester has EXECUTE permission.
# @POST Returns sync summary with synced/failed counts after attempting all environments. # @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] # #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. # @BRIEF Defines the FastAPI router for plugin-related endpoints, allowing clients to list available plugins.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [PluginConfig] # @RELATION DEPENDS_ON -> [PluginConfig]
@@ -15,6 +16,7 @@ router = APIRouter()
# #region list_plugins [TYPE Function] # #region list_plugins [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a list of all available plugins. # @BRIEF Retrieve a list of all available plugins.
# @PRE plugin_loader is injected via Depends. # @PRE plugin_loader is injected via Depends.
# @POST Returns a list of PluginConfig objects. # @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] # #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. # @BRIEF Exposes self-scoped profile preference endpoints and environment-based Superset account lookup.
# @LAYER API # @LAYER API
@@ -60,6 +61,7 @@ def _get_profile_service(db: Session, config_manager, plugin_loader=None) -> Pro
# #region get_preferences [TYPE Function] # #region get_preferences [TYPE Function]
# @ingroup Api
# @RELATION CALLS -> ProfileService # @RELATION CALLS -> ProfileService
# @BRIEF Get authenticated user's dashboard filter preference. # @BRIEF Get authenticated user's dashboard filter preference.
# @PRE Valid JWT and authenticated user context. # @PRE Valid JWT and authenticated user context.
@@ -79,6 +81,7 @@ async def get_preferences(
# #region update_preferences [TYPE Function] # #region update_preferences [TYPE Function]
# @ingroup Api
# @RELATION CALLS -> ProfileService # @RELATION CALLS -> ProfileService
# @BRIEF Update authenticated user's dashboard filter preference. # @BRIEF Update authenticated user's dashboard filter preference.
# @PRE Valid JWT and valid request payload. # @PRE Valid JWT and valid request payload.
@@ -106,6 +109,7 @@ async def update_preferences(
# #region lookup_superset_accounts [TYPE Function] # #region lookup_superset_accounts [TYPE Function]
# @ingroup Api
# @RELATION CALLS -> ProfileService # @RELATION CALLS -> ProfileService
# @BRIEF Lookup Superset account candidates in selected environment. # @BRIEF Lookup Superset account candidates in selected environment.
# @PRE Valid JWT, authenticated context, and environment_id query parameter. # @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] # #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. # @BRIEF FastAPI router for unified task report list and detail retrieval endpoints.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [ReportsService] # @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] # #region list_reports [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return paginated unified reports list. # @BRIEF Return paginated unified reports list.
# @PRE authenticated/authorized request and validated query params. # @PRE authenticated/authorized request and validated query params.
# @POST returns {items,total,page,page_size,has_next,applied_filters}. # @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] # #region get_report_detail [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Return one normalized report detail with diagnostics and next actions. # @BRIEF Return one normalized report detail with diagnostics and next actions.
# @PRE authenticated/authorized request and existing report_id. # @PRE authenticated/authorized request and existing report_id.
# @POST returns normalized detail envelope or 404 when report is not found. # @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] # #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. # @BRIEF Provides API endpoints for managing application settings and Superset environments.
# @LAYER API # @LAYER API
@@ -91,6 +92,7 @@ async def _validate_superset_connection_fast(env: Environment) -> None:
# #region get_settings [C:2] [TYPE Function] # #region get_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves all application settings. # @BRIEF Retrieves all application settings.
# @PRE Config manager is available. # @PRE Config manager is available.
# @POST Returns masked AppConfig. # @POST Returns masked AppConfig.
@@ -145,6 +147,7 @@ async def get_allowed_languages(
# #region update_global_settings [C:2] [TYPE Function] # #region update_global_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates global application settings. # @BRIEF Updates global application settings.
# @PRE New settings are provided. # @PRE New settings are provided.
# @POST Global settings are updated. # @POST Global settings are updated.
@@ -165,6 +168,7 @@ async def update_global_settings(
# #region get_storage_settings [C:2] [TYPE Function] # #region get_storage_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves storage-specific settings. # @BRIEF Retrieves storage-specific settings.
@router.get("/storage", response_model=StorageConfig) @router.get("/storage", response_model=StorageConfig)
async def get_storage_settings( async def get_storage_settings(
@@ -179,6 +183,7 @@ async def get_storage_settings(
# #region update_storage_settings [C:2] [TYPE Function] # #region update_storage_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates storage-specific settings. # @BRIEF Updates storage-specific settings.
# @POST Storage settings are updated and saved. # @POST Storage settings are updated and saved.
@router.put("/storage", response_model=StorageConfig) @router.put("/storage", response_model=StorageConfig)
@@ -202,6 +207,7 @@ async def update_storage_settings(
# #region get_environments [C:2] [TYPE Function] # #region get_environments [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all configured Superset environments. # @BRIEF Lists all configured Superset environments.
# @PRE Config manager is available. # @PRE Config manager is available.
# @POST Returns list of environments. # @POST Returns list of environments.
@@ -223,6 +229,7 @@ async def get_environments(
# #region add_environment [C:2] [TYPE Function] # #region add_environment [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Adds a new Superset environment. # @BRIEF Adds a new Superset environment.
# @PRE Environment data is valid and reachable. # @PRE Environment data is valid and reachable.
# @POST Environment is added to config. # @POST Environment is added to config.
@@ -255,6 +262,7 @@ async def add_environment(
# #region update_environment [C:2] [TYPE Function] # #region update_environment [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing Superset environment. # @BRIEF Updates an existing Superset environment.
# @PRE ID and valid environment data are provided. # @PRE ID and valid environment data are provided.
# @POST Environment is updated in config. # @POST Environment is updated in config.
@@ -298,6 +306,7 @@ async def update_environment(
# #region delete_environment [C:2] [TYPE Function] # #region delete_environment [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Deletes a Superset environment. # @BRIEF Deletes a Superset environment.
# @PRE ID is provided. # @PRE ID is provided.
# @POST Environment is removed from config. # @POST Environment is removed from config.
@@ -315,6 +324,7 @@ async def delete_environment(
# #region test_environment_connection [C:2] [TYPE Function] # #region test_environment_connection [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Tests the connection to a Superset environment. # @BRIEF Tests the connection to a Superset environment.
# @PRE ID is provided. # @PRE ID is provided.
# @POST Returns success or error status. # @POST Returns success or error status.
@@ -348,6 +358,7 @@ async def test_environment_connection(
# #region get_logging_config [C:2] [TYPE Function] # #region get_logging_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves current logging configuration. # @BRIEF Retrieves current logging configuration.
# @PRE Config manager is available. # @PRE Config manager is available.
# @POST Returns logging configuration. # @POST Returns logging configuration.
@@ -369,6 +380,7 @@ async def get_logging_config(
# #region update_logging_config [C:2] [TYPE Function] # #region update_logging_config [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates logging configuration. # @BRIEF Updates logging configuration.
# @PRE New logging config is provided. # @PRE New logging config is provided.
# @POST Logging configuration is updated and saved. # @POST Logging configuration is updated and saved.
@@ -417,6 +429,7 @@ class ConsolidatedSettingsResponse(BaseModel):
# #region get_consolidated_settings [C:4] [TYPE Function] # #region get_consolidated_settings [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieves all settings categories in a single call. # @BRIEF Retrieves all settings categories in a single call.
# @PRE Config manager is available and the caller holds admin settings read permission. # @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. # @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] # #region update_consolidated_settings [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Bulk update application settings from the consolidated view. # @BRIEF Bulk update application settings from the consolidated view.
# @PRE User has admin permissions, config is valid. # @PRE User has admin permissions, config is valid.
# @POST Settings are updated and saved via ConfigManager. # @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] # #region get_validation_policies [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all validation policies. # @BRIEF Lists all validation policies.
@router.get("/automation/policies", response_model=list[ValidationPolicyResponse]) @router.get("/automation/policies", response_model=list[ValidationPolicyResponse])
async def get_validation_policies( async def get_validation_policies(
@@ -582,6 +597,7 @@ async def get_validation_policies(
# #region create_validation_policy [C:2] [TYPE Function] # #region create_validation_policy [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Creates a new validation policy. # @BRIEF Creates a new validation policy.
@router.post("/automation/policies", response_model=ValidationPolicyResponse) @router.post("/automation/policies", response_model=ValidationPolicyResponse)
async def create_validation_policy( async def create_validation_policy(
@@ -601,6 +617,7 @@ async def create_validation_policy(
# #region update_validation_policy [C:2] [TYPE Function] # #region update_validation_policy [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Updates an existing validation policy. # @BRIEF Updates an existing validation policy.
@router.patch("/automation/policies/{id}", response_model=ValidationPolicyResponse) @router.patch("/automation/policies/{id}", response_model=ValidationPolicyResponse)
async def update_validation_policy( async def update_validation_policy(
@@ -627,6 +644,7 @@ async def update_validation_policy(
# #region delete_validation_policy [C:2] [TYPE Function] # #region delete_validation_policy [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Deletes a validation policy. # @BRIEF Deletes a validation policy.
@router.delete("/automation/policies/{id}") @router.delete("/automation/policies/{id}")
async def delete_validation_policy( async def delete_validation_policy(
@@ -648,6 +666,7 @@ async def delete_validation_policy(
# #region get_translation_schedules [C:2] [TYPE Function] # #region get_translation_schedules [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Lists all translation schedules joined with translation job names. # @BRIEF Lists all translation schedules joined with translation job names.
@router.get("/automation/translation-schedules", response_model=list[TranslationScheduleItem]) @router.get("/automation/translation-schedules", response_model=list[TranslationScheduleItem])
async def get_translation_schedules( async def get_translation_schedules(

View File

@@ -1,4 +1,5 @@
# #region storage_routes [C:5] [TYPE Module] [SEMANTICS fastapi, storage, api, upload, download] # #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). # @BRIEF API endpoints for file storage management (backups and repositories).
# @LAYER API # @LAYER API
@@ -20,6 +21,7 @@ from ...plugins.storage.plugin import StoragePlugin
router = APIRouter(tags=["storage"]) router = APIRouter(tags=["storage"])
# #region list_files [C:3] [TYPE Function] # #region list_files [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List all files and directories in the storage system. # @BRIEF List all files and directories in the storage system.
# #
# @PRE None. # @PRE None.
@@ -42,6 +44,7 @@ async def list_files(
# #endregion list_files # #endregion list_files
# #region upload_file [C:3] [TYPE Function] # #region upload_file [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Upload a file to the storage system. # @BRIEF Upload a file to the storage system.
# #
# @PRE category must be a valid FileCategory. # @PRE category must be a valid FileCategory.
@@ -71,6 +74,7 @@ async def upload_file(
# #endregion upload_file # #endregion upload_file
# #region delete_file [C:3] [TYPE Function] # #region delete_file [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a specific file or directory. # @BRIEF Delete a specific file or directory.
# #
# @PRE category must be a valid FileCategory. # @PRE category must be a valid FileCategory.
@@ -100,6 +104,7 @@ async def delete_file(
# #endregion delete_file # #endregion delete_file
# #region download_file [C:3] [TYPE Function] # #region download_file [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a file for download. # @BRIEF Retrieve a file for download.
# #
# @PRE category must be a valid FileCategory. # @PRE category must be a valid FileCategory.
@@ -129,6 +134,7 @@ async def download_file(
# #endregion download_file # #endregion download_file
# #region get_file_by_path [C:3] [TYPE Function] # #region get_file_by_path [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a file by validated absolute/relative path under storage root. # @BRIEF Retrieve a file by validated absolute/relative path under storage root.
# #
# @PRE path must resolve under configured 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] # #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. # @BRIEF Defines the FastAPI router for task-related endpoints, allowing clients to create, list, and get the status of tasks.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [TaskManager] # @RELATION DEPENDS_ON -> [TaskManager]
@@ -48,6 +49,7 @@ class ResumeTaskRequest(BaseModel):
# #region create_task [C:3] [TYPE Function] # #region create_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Create and start a new task for a given plugin. # @BRIEF Create and start a new task for a given plugin.
# @PRE plugin_id must exist and params must be valid for that plugin. # @PRE plugin_id must exist and params must be valid for that plugin.
# @POST A new task is created and started. # @POST A new task is created and started.
@@ -132,6 +134,7 @@ async def create_task(
# #region list_tasks [C:2] [TYPE Function] # #region list_tasks [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve a list of tasks with pagination and optional status filter. # @BRIEF Retrieve a list of tasks with pagination and optional status filter.
# @PRE task_manager must be available. # @PRE task_manager must be available.
# @POST Returns a list of tasks. # @POST Returns a list of tasks.
@@ -177,6 +180,7 @@ async def list_tasks(
# #region get_task [C:2] [TYPE Function] # #region get_task [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve the details of a specific task. # @BRIEF Retrieve the details of a specific task.
# @PRE task_id must exist. # @PRE task_id must exist.
# @POST Returns task details or raises 404. # @POST Returns task details or raises 404.
@@ -200,6 +204,7 @@ async def get_task(
# #region get_task_logs [C:5] [TYPE Function] # #region get_task_logs [C:5] [TYPE Function]
# @ingroup Api
# @BRIEF Retrieve logs for a specific task with optional filtering. # @BRIEF Retrieve logs for a specific task with optional filtering.
# @PRE task_id must exist. # @PRE task_id must exist.
# @POST Returns a list of log entries or raises 404. # @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] # #region get_task_log_stats [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get statistics about logs for a task (counts by level and source). # @BRIEF Get statistics about logs for a task (counts by level and source).
# @PRE task_id must exist. # @PRE task_id must exist.
# @POST Returns log statistics or raises 404. # @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] # #region get_task_log_sources [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Get unique sources for a task's logs. # @BRIEF Get unique sources for a task's logs.
# @PRE task_id must exist. # @PRE task_id must exist.
# @POST Returns list of unique source names or raises 404. # @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] # #region resolve_task [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Resolve a task that is awaiting mapping. # @BRIEF Resolve a task that is awaiting mapping.
# @PRE task must be in AWAITING_MAPPING status. # @PRE task must be in AWAITING_MAPPING status.
# @POST Task is resolved and resumes execution. # @POST Task is resolved and resumes execution.
@@ -337,6 +345,7 @@ async def resolve_task(
# #region resume_task [C:2] [TYPE Function] # #region resume_task [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Resume a task that is awaiting input (e.g., passwords). # @BRIEF Resume a task that is awaiting input (e.g., passwords).
# @PRE task must be in AWAITING_INPUT status. # @PRE task must be in AWAITING_INPUT status.
# @POST Task resumes execution with provided input. # @POST Task resumes execution with provided input.
@@ -360,6 +369,7 @@ async def resume_task(
# #region clear_tasks [C:2] [TYPE Function] # #region clear_tasks [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Clear tasks matching the status filter. # @BRIEF Clear tasks matching the status filter.
# @PRE task_manager is available. # @PRE task_manager is available.
# @POST Tasks are removed from memory/persistence. # @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] # #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. # @BRIEF API routes for LLM-based SQL/dashboard translation management including terminology dictionary CRUD and import.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [SupersetClient] # @RELATION DEPENDS_ON -> [SupersetClient]

View File

@@ -1,4 +1,5 @@
# #region TranslateCorrectionRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search] # #region TranslateCorrectionRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]
# @defgroup Api Module group.
# @BRIEF Term correction submission endpoints. # @BRIEF Term correction submission endpoints.
# @LAYER API # @LAYER API
@@ -21,6 +22,7 @@ from ._router import router
# ============================================================ # ============================================================
# #region submit_correction [C:4] [TYPE Function] # #region submit_correction [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Submit a term correction from a run result review. # @BRIEF Submit a term correction from a run result review.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST Correction applied with conflict detection. # @POST Correction applied with conflict detection.
@@ -58,6 +60,7 @@ async def submit_correction(
# #region submit_bulk_corrections [C:4] [TYPE Function] # #region submit_bulk_corrections [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Submit multiple term corrections atomically. # @BRIEF Submit multiple term corrections atomically.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST All corrections applied or none with conflict list. # @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] # #region TranslateDictionaryRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]
# @defgroup Api Module group.
# @BRIEF Terminology Dictionary CRUD, entries management, and import routes. # @BRIEF Terminology Dictionary CRUD, entries management, and import routes.
# @LAYER API # @LAYER API
@@ -23,6 +24,7 @@ from ._router import router
# ============================================================ # ============================================================
# #region list_dictionaries [C:4] [TYPE Function] # #region list_dictionaries [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List all terminology dictionaries. # @BRIEF List all terminology dictionaries.
# @PRE User has translate.dictionary.view permission. # @PRE User has translate.dictionary.view permission.
# @POST Returns list of dictionaries with total count. # @POST Returns list of dictionaries with total count.
@@ -47,6 +49,7 @@ async def list_dictionaries(
# #region get_dictionary [C:4] [TYPE Function] # #region get_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get a single terminology dictionary by ID. # @BRIEF Get a single terminology dictionary by ID.
# @PRE User has translate.dictionary.view permission. # @PRE User has translate.dictionary.view permission.
# @POST Returns the dictionary with entry_count. # @POST Returns the dictionary with entry_count.
@@ -72,6 +75,7 @@ async def get_dictionary(
# #region create_dictionary [C:4] [TYPE Function] # #region create_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new terminology dictionary. # @BRIEF Create a new terminology dictionary.
# @PRE User has translate.dictionary.create permission. # @PRE User has translate.dictionary.create permission.
# @POST Returns the created dictionary. # @POST Returns the created dictionary.
@@ -98,6 +102,7 @@ async def create_dictionary(
# #region update_dictionary [C:4] [TYPE Function] # #region update_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing terminology dictionary. # @BRIEF Update an existing terminology dictionary.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST Returns the updated dictionary. # @POST Returns the updated dictionary.
@@ -130,6 +135,7 @@ async def update_dictionary(
# #region delete_dictionary [C:4] [TYPE Function] # #region delete_dictionary [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs. # @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs.
# @PRE User has translate.dictionary.delete permission. # @PRE User has translate.dictionary.delete permission.
# @POST Dictionary is deleted. # @POST Dictionary is deleted.
@@ -159,6 +165,7 @@ async def delete_dictionary(
# ============================================================ # ============================================================
# #region list_dictionary_entries [C:4] [TYPE Function] # #region list_dictionary_entries [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List entries for a dictionary, optionally filtered by language pair. # @BRIEF List entries for a dictionary, optionally filtered by language pair.
# @PRE User has translate.dictionary.view permission. # @PRE User has translate.dictionary.view permission.
# @POST Returns paginated list of entries with language pair fields. # @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] # #region add_dictionary_entry [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Add a new entry to a dictionary. # @BRIEF Add a new entry to a dictionary.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST Entry is created. # @POST Entry is created.
@@ -269,6 +277,7 @@ async def add_dictionary_entry(
# #region edit_dictionary_entry [C:4] [TYPE Function] # #region edit_dictionary_entry [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Update an existing dictionary entry. # @BRIEF Update an existing dictionary entry.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST Entry is updated. # @POST Entry is updated.
@@ -321,6 +330,7 @@ async def edit_dictionary_entry(
# #region delete_dictionary_entry [C:4] [TYPE Function] # #region delete_dictionary_entry [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a dictionary entry. # @BRIEF Delete a dictionary entry.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST Entry is deleted. # @POST Entry is deleted.
@@ -345,6 +355,7 @@ async def delete_dictionary_entry(
# #region import_dictionary_entries [C:4] [TYPE Function] # #region import_dictionary_entries [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Import entries into a terminology dictionary from CSV/TSV content. # @BRIEF Import entries into a terminology dictionary from CSV/TSV content.
# @PRE User has translate.dictionary.edit permission. # @PRE User has translate.dictionary.edit permission.
# @POST Entries are imported per conflict mode. # @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] # #region TranslateHelpersModule [C:2] [TYPE Module] [SEMANTICS sqlalchemy, translate, helper, query, mapping]
# @defgroup Api Module group.
# @BRIEF Shared helper functions for translate route handlers. # @BRIEF Shared helper functions for translate route handlers.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [EXT:frontend:models.translate] # @RELATION DEPENDS_ON -> [EXT:frontend:models.translate]

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
from fastapi import APIRouter from fastapi import APIRouter
# #region translate_router [TYPE Global] # #region translate_router [TYPE Global]
# @ingroup Api
# @BRIEF APIRouter instance for all translate sub-routes. # @BRIEF APIRouter instance for all translate sub-routes.
router = APIRouter(prefix="/api/translate", tags=["translate"]) 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] # #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. # @BRIEF Translation Run inline edit, bulk find-replace, and language override routes.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunRoutesModule] # @RELATION DEPENDS_ON -> [TranslateRunRoutesModule]
@@ -21,6 +22,7 @@ from ._router import router
# #region override_detected_language [C:4] [TYPE Function] # #region override_detected_language [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Manually override the detected source language for a specific translation language entry. # @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. # @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. # @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] # #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. # @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. # @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. # @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] # #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. # @BRIEF Perform bulk find-and-replace on translated values within a run.
# @PRE User has translate.job.execute permission. Run exists. # @PRE User has translate.job.execute permission. Run exists.
# @POST If preview=false, matching translations are updated. Optional dictionary submission. # @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] # #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. # @BRIEF Translation Run history, status, records and batches routes.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunRoutesModule] # @RELATION DEPENDS_ON -> [TranslateRunRoutesModule]
@@ -20,6 +21,7 @@ from datetime import datetime, UTC
# #region get_run_history [C:4] [TYPE Function] # #region get_run_history [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get run history for a translation job. # @BRIEF Get run history for a translation job.
# @PRE User has translate.history.view permission. # @PRE User has translate.history.view permission.
# @POST Returns list of runs. # @POST Returns list of runs.
@@ -44,6 +46,7 @@ def get_run_history(
# #region get_run_status [C:4] [TYPE Function] # #region get_run_status [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get status and statistics for a translation run. # @BRIEF Get status and statistics for a translation run.
# @PRE User has translate.history.view permission. # @PRE User has translate.history.view permission.
# @POST Returns run details with statistics. # @POST Returns run details with statistics.
@@ -65,6 +68,7 @@ def get_run_status(
# #region get_run_records [C:4] [TYPE Function] # #region get_run_records [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get paginated records for a translation run. # @BRIEF Get paginated records for a translation run.
# @PRE User has translate.history.view permission. # @PRE User has translate.history.view permission.
# @POST Returns paginated records. # @POST Returns paginated records.
@@ -90,6 +94,7 @@ def get_run_records(
# #region get_batches [C:4] [TYPE Function] # #region get_batches [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get batches for a translation run. # @BRIEF Get batches for a translation run.
# @PRE User has translate.job.view permission. # @PRE User has translate.job.view permission.
# @POST Returns list of batches. # @POST Returns list of batches.

View File

@@ -1,4 +1,5 @@
# #region TranslateRunListRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, download, search] # #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). # @BRIEF Translation Run listing, detail, and CSV download routes (cross-job).
# @LAYER API # @LAYER API
@@ -20,6 +21,7 @@ from ._router import router
# ============================================================ # ============================================================
# #region list_runs [C:4] [TYPE Function] # #region list_runs [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF List all runs with cross-job filtering and pagination. # @BRIEF List all runs with cross-job filtering and pagination.
# @PRE User has translate.history.view permission. # @PRE User has translate.history.view permission.
# @POST Returns paginated list of runs. # @POST Returns paginated list of runs.
@@ -128,6 +130,7 @@ async def list_runs(
# ============================================================ # ============================================================
# #region get_run_detail [C:4] [TYPE Function] # #region get_run_detail [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Get detailed run info with config_snapshot, records, events. # @BRIEF Get detailed run info with config_snapshot, records, events.
# @PRE User has translate.history.view permission. # @PRE User has translate.history.view permission.
# @POST Returns run detail with records and events. # @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] # #region download_skipped_csv [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Download a CSV of skipped translation records from a run. # @BRIEF Download a CSV of skipped translation records from a run.
# @PRE User has translate.job.view permission. # @PRE User has translate.job.view permission.
# @POST Returns CSV file of skipped records. # @POST Returns CSV file of skipped records.
@@ -229,6 +233,7 @@ async def download_skipped_csv(
# ============================================================ # ============================================================
# #region download_failed_csv [C:4] [TYPE Function] # #region download_failed_csv [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Download a CSV of successfully translated but uninserted records from a FAILED run. # @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. # @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. # @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] # #region TranslateRunRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, execution]
# @defgroup Api Module group.
# @BRIEF Translation Run execution, retry, and cancel routes. # @BRIEF Translation Run execution, retry, and cancel routes.
# @LAYER API # @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunHistoryRoutesModule] # @RELATION DEPENDS_ON -> [TranslateRunHistoryRoutesModule]
@@ -23,6 +24,7 @@ from ._router import router
# #region run_translation [C:4] [TYPE Function] # #region run_translation [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Execute a translation job (trigger a run). # @BRIEF Execute a translation job (trigger a run).
# @PRE User has translate.job.execute permission. # @PRE User has translate.job.execute permission.
# @POST Returns the created translation run. # @POST Returns the created translation run.
@@ -128,6 +130,7 @@ async def run_translation(
# #region retry_run [C:4] [TYPE Function] # #region retry_run [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retry failed batches in a translation run. # @BRIEF Retry failed batches in a translation run.
# @PRE User has translate.job.execute permission. # @PRE User has translate.job.execute permission.
# @POST Returns the updated translation run. # @POST Returns the updated translation run.
@@ -154,6 +157,7 @@ async def retry_run(
# #region retry_insert [C:4] [TYPE Function] # #region retry_insert [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retry the SQL insert phase for a completed run. # @BRIEF Retry the SQL insert phase for a completed run.
# @PRE User has translate.job.execute permission. # @PRE User has translate.job.execute permission.
# @POST Returns the updated run. # @POST Returns the updated run.
@@ -180,6 +184,7 @@ async def retry_insert(
# #region cancel_run [C:4] [TYPE Function] # #region cancel_run [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Cancel a running translation. # @BRIEF Cancel a running translation.
# @PRE User has translate.job.execute permission. # @PRE User has translate.job.execute permission.
# @POST Run is cancelled. # @POST Run is cancelled.

View File

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

View File

@@ -35,18 +35,19 @@ from unittest.mock import AsyncMock, MagicMock
from fastapi.testclient import TestClient 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.app import app
from src.dependencies import ( from src.dependencies import (
get_config_manager, get_config_manager,
get_current_user, get_current_user,
get_scheduler_service,
get_task_manager, get_task_manager,
) )
from src.models.auth import User from src.models.auth import User
from src.schemas.validation import ( from src.schemas.validation import (
TriggerRunResponse, TriggerRunResponse,
ValidationRunDetailResponse, RunDetailResponse,
ValidationRunListResponse, RunListResponse,
ValidationTaskListResponse, ValidationTaskListResponse,
ValidationTaskResponse, ValidationTaskResponse,
) )
@@ -78,19 +79,20 @@ def mock_all_deps():
# Reset user state — Admin role passes all permission gates # Reset user state — Admin role passes all permission gates
mock_user.roles = [admin_role] mock_user.roles = [admin_role]
mock_task_service = MagicMock() mock_validation_service = MagicMock()
mock_run_service = MagicMock()
mock_task_manager = MagicMock() mock_task_manager = MagicMock()
app.dependency_overrides[_get_task_service] = lambda: mock_task_service mock_scheduler_service = MagicMock()
app.dependency_overrides[_get_run_service] = lambda: mock_run_service
app.dependency_overrides[_get_task_service] = lambda: mock_validation_service
app.dependency_overrides[get_config_manager] = lambda: MagicMock() app.dependency_overrides[get_config_manager] = lambda: MagicMock()
app.dependency_overrides[get_task_manager] = lambda: mock_task_manager 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 app.dependency_overrides[get_current_user] = lambda: mock_user
yield { yield {
"task_service": mock_task_service, "task_service": mock_validation_service,
"run_service": mock_run_service, "run_service": mock_validation_service,
"task_manager": mock_task_manager, "task_manager": mock_task_manager,
} }
app.dependency_overrides.clear() app.dependency_overrides.clear()
@@ -146,7 +148,7 @@ def test_list_tasks_success(mock_all_deps):
task = _make_task_response() task = _make_task_response()
mock_all_deps["task_service"].list_tasks.return_value = (1, [task]) 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 assert response.status_code == 200
data = response.json() data = response.json()
assert data["items"][0]["name"] == "Test Task" assert data["items"][0]["name"] == "Test Task"
@@ -156,7 +158,7 @@ def test_list_tasks_success(mock_all_deps):
ValidationTaskListResponse(**data) ValidationTaskListResponse(**data)
# environment_id filter # 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 assert response.status_code == 200
mock_all_deps["task_service"].list_tasks.assert_called_with( mock_all_deps["task_service"].list_tasks.assert_called_with(
is_active=None, environment_id="env-1", page=1, page_size=20 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] # #region test_list_tasks_pagination [C:2] [TYPE Function]
# @BRIEF Pagination query params are validated: page >= 1, page_size 1-100. # @BRIEF Pagination query params are validated: page >= 1, page_size 1-100.
def test_list_tasks_pagination(mock_all_deps): 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 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 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 assert response.status_code == 422
# #endregion test_list_tasks_pagination # #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. # @BRIEF Service ValueError becomes 400 on list_tasks.
def test_list_tasks_service_error(mock_all_deps): def test_list_tasks_service_error(mock_all_deps):
mock_all_deps["task_service"].list_tasks.side_effect = ValueError("Bad filter") 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 response.status_code == 400
assert "Bad filter" in response.json()["detail"] assert "Bad filter" in response.json()["detail"]
# #endregion test_list_tasks_service_error # #endregion test_list_tasks_service_error
@@ -201,7 +203,7 @@ def test_create_task_success(mock_all_deps):
"notify_owners": True, "notify_owners": True,
"alert_condition": "FAIL_ONLY", "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 assert response.status_code == 201
data = response.json() data = response.json()
assert data["name"] == "Test Task" 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. # @BRIEF POST with missing required fields returns 422.
def test_create_task_missing_fields(mock_all_deps): def test_create_task_missing_fields(mock_all_deps):
# Empty payload # Empty payload
response = client.post("/api/validation/tasks", json={}) response = client.post("/api/validation-tasks", json={})
assert response.status_code == 422 assert response.status_code == 422
errors = response.json() errors = response.json()
assert "detail" in errors assert "detail" in errors
# Missing provider_id # 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"], "name": "Test", "environment_id": "env-1", "dashboard_ids": ["dash-1"],
}) })
assert response.status_code == 422 assert response.status_code == 422
# Missing dashboard_ids # 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", "name": "Test", "environment_id": "env-1", "provider_id": "provider-1",
}) })
assert response.status_code == 422 assert response.status_code == 422
@@ -244,7 +246,7 @@ def test_create_task_invalid_provider(mock_all_deps):
"name": "Test", "environment_id": "env-1", "name": "Test", "environment_id": "env-1",
"dashboard_ids": ["dash-1"], "provider_id": "provider-2", "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 response.status_code == 422
assert "not multimodal" in response.json()["detail"] assert "not multimodal" in response.json()["detail"]
# #endregion test_create_task_invalid_provider # #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["task_service"].get_task.return_value = _make_task_response()
mock_all_deps["run_service"].list_runs.return_value = (0, []) 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 assert response.status_code == 200
data = response.json() data = response.json()
assert "task" in data assert "task" in data
@@ -270,7 +272,7 @@ def test_get_task_success(mock_all_deps):
# @BRIEF GET for nonexistent task returns 404. # @BRIEF GET for nonexistent task returns 404.
def test_get_task_not_found(mock_all_deps): def test_get_task_not_found(mock_all_deps):
mock_all_deps["task_service"].get_task.side_effect = ValueError("not found") 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 assert response.status_code == 404
# #endregion test_get_task_not_found # #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 mock_all_deps["task_service"].update_task.return_value = updated
payload = {"name": "Updated Name", "is_active": False} 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 assert response.status_code == 200
data = response.json() data = response.json()
assert data["name"] == "Updated Name" 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): def test_update_task_not_found(mock_all_deps):
mock_all_deps["task_service"].update_task.side_effect = ValueError("not found") mock_all_deps["task_service"].update_task.side_effect = ValueError("not found")
payload = {"name": "Nope"} 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 assert response.status_code == 404
# #endregion test_update_task_not_found # #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. # @BRIEF DELETE /validation/tasks/{id} returns 204.
def test_delete_task_success(mock_all_deps): def test_delete_task_success(mock_all_deps):
mock_all_deps["task_service"].delete_task.return_value = None 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 assert response.status_code == 204
mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=False) mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=False)
# #endregion test_delete_task_success # #endregion test_delete_task_success
@@ -316,7 +318,7 @@ def test_delete_task_success(mock_all_deps):
# @BRIEF DELETE for nonexistent task returns 404. # @BRIEF DELETE for nonexistent task returns 404.
def test_delete_task_not_found(mock_all_deps): def test_delete_task_not_found(mock_all_deps):
mock_all_deps["task_service"].delete_task.side_effect = ValueError("not found") 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 assert response.status_code == 404
# #endregion test_delete_task_not_found # #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. # @BRIEF DELETE with delete_runs=true passes query param to service.
def test_delete_task_with_runs_flag(mock_all_deps): def test_delete_task_with_runs_flag(mock_all_deps):
mock_all_deps["task_service"].delete_task.return_value = None 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 assert response.status_code == 204
mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=True) mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=True)
# #endregion test_delete_task_with_runs_flag # #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_service"].trigger_run = AsyncMock(return_value="spawned-1")
mock_all_deps["task_manager"].create_task = AsyncMock() 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 assert response.status_code == 200
data = response.json() data = response.json()
assert data["task_id"] == "task-1" 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( mock_all_deps["task_service"].trigger_run = AsyncMock(
side_effect=ValueError("Validation task 'nonexistent' not found") 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 response.status_code == 422
assert "not found" in response.json()["detail"] assert "not found" in response.json()["detail"]
# #endregion test_trigger_run_not_found # #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( mock_all_deps["task_service"].trigger_run = AsyncMock(
side_effect=ValueError("has no dashboard IDs configured") 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 response.status_code == 422
assert "no dashboard IDs" in response.json()["detail"] assert "no dashboard IDs" in response.json()["detail"]
# #endregion test_trigger_run_no_dashboards # #endregion test_trigger_run_no_dashboards
@@ -379,12 +381,12 @@ def test_list_runs_success(mock_all_deps):
run = _make_run_response() run = _make_run_response()
mock_all_deps["run_service"].list_runs.return_value = (1, [run]) 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 assert response.status_code == 200
data = response.json() data = response.json()
assert data["items"][0]["id"] == "run-1" assert data["items"][0]["id"] == "run-1"
assert data["total"] == 1 assert data["total"] == 1
ValidationRunListResponse(**data) RunListResponse(**data)
# #endregion test_list_runs_success # #endregion test_list_runs_success
@@ -402,7 +404,7 @@ def test_list_runs_filters(mock_all_deps):
"?date_to=2026-12-31", "?date_to=2026-12-31",
"?status=PASS&dashboard_id=dash-1&date_from=2026-01-01", "?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}" assert response.status_code == 200, f"Filter query failed: {query}"
# #endregion test_list_runs_filters # #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] # #region test_list_runs_pagination [C:2] [TYPE Function]
# @BRIEF Pagination boundary checks on runs list. # @BRIEF Pagination boundary checks on runs list.
def test_list_runs_pagination(mock_all_deps): 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 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 assert response.status_code == 422
# #endregion test_list_runs_pagination # #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. # @BRIEF Status is a free string; any value passes through to service.
def test_list_runs_invalid_status(mock_all_deps): def test_list_runs_invalid_status(mock_all_deps):
mock_all_deps["run_service"].list_runs.return_value = (0, []) 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 assert response.status_code == 200
call_kwargs = mock_all_deps["run_service"].list_runs.call_args[1] call_kwargs = mock_all_deps["run_service"].list_runs.call_args[1]
assert call_kwargs.get("status") == "INVALID_STATUS_XYZ" 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] # #region test_get_run_detail_success [C:2] [TYPE Function]
# @BRIEF GET /validation/runs/{id} returns full detail with issues and raw_response. # @BRIEF GET /validation/runs/{id} returns full detail with issues and raw_response.
def test_get_run_detail_success(mock_all_deps): 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", id="run-1",
task_id="task-1", task_id="task-1",
dashboard_id="dash-1", dashboard_id="dash-1",
@@ -446,13 +448,13 @@ def test_get_run_detail_success(mock_all_deps):
task_name="Test Task", 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 assert response.status_code == 200
data = response.json() data = response.json()
assert data["status"] == "FAIL" assert data["status"] == "FAIL"
assert len(data["issues"]) == 1 assert len(data["issues"]) == 1
assert data["raw_response"] is not None assert data["raw_response"] is not None
ValidationRunDetailResponse(**data) RunDetailResponse(**data)
# #endregion test_get_run_detail_success # #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. # @BRIEF GET for nonexistent run returns 404.
def test_get_run_detail_not_found(mock_all_deps): def test_get_run_detail_not_found(mock_all_deps):
mock_all_deps["run_service"].get_run_detail.side_effect = ValueError("not found") 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 assert response.status_code == 404
# #endregion test_get_run_detail_not_found # #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. # @BRIEF DELETE /validation/runs/{id} returns 204.
def test_delete_run_success(mock_all_deps): def test_delete_run_success(mock_all_deps):
mock_all_deps["run_service"].delete_run.return_value = True 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 assert response.status_code == 204
mock_all_deps["run_service"].delete_run.assert_called_with("run-1") mock_all_deps["run_service"].delete_run.assert_called_with("run-1")
# #endregion test_delete_run_success # #endregion test_delete_run_success
@@ -479,7 +481,7 @@ def test_delete_run_success(mock_all_deps):
# @BRIEF DELETE for nonexistent run returns 404. # @BRIEF DELETE for nonexistent run returns 404.
def test_delete_run_not_found(mock_all_deps): def test_delete_run_not_found(mock_all_deps):
mock_all_deps["run_service"].delete_run.return_value = False 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 assert response.status_code == 404
# #endregion test_delete_run_not_found # #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): def test_endpoints_require_authentication(mock_all_deps):
app.dependency_overrides.pop(get_current_user, None) app.dependency_overrides.pop(get_current_user, None)
endpoints = [ endpoints = [
("GET", "/api/validation/tasks"), ("GET", "/api/validation-tasks"),
("POST", "/api/validation/tasks"), ("POST", "/api/validation-tasks"),
("GET", "/api/validation/tasks/task-1"), ("GET", "/api/validation-tasks/task-1"),
("PUT", "/api/validation/tasks/task-1"), ("PUT", "/api/validation-tasks/task-1"),
("DELETE", "/api/validation/tasks/task-1"), ("DELETE", "/api/validation-tasks/task-1"),
("POST", "/api/validation/tasks/task-1/run"), ("POST", "/api/validation-tasks/task-1/run"),
("GET", "/api/validation/runs"), ("GET", "/api/validation-runs"),
("GET", "/api/validation/runs/run-1"), ("GET", "/api/validation-runs/run-1"),
("DELETE", "/api/validation/runs/run-1"), ("DELETE", "/api/validation-runs/run-1"),
] ]
for method, path in endpoints: for method, path in endpoints:
response = client.request(method, path) 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): def test_permission_denied_non_admin(mock_all_deps):
"""Remove Admin role so has_permission raises 403.""" """Remove Admin role so has_permission raises 403."""
mock_user.roles = [] # No Admin, no permissions mock_user.roles = [] # No Admin, no permissions
response = client.get("/api/validation/tasks") response = client.get("/api/validation-tasks")
assert response.status_code == 403 assert response.status_code == 403
# #endregion test_permission_denied_non_admin # #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): def test_permission_denied_all_actions(mock_all_deps):
mock_user.roles = [] mock_user.roles = []
action_endpoints = [ action_endpoints = [
("GET", "/api/validation/tasks"), ("GET", "/api/validation-tasks"),
("POST", "/api/validation/tasks"), ("POST", "/api/validation-tasks"),
("GET", "/api/validation/tasks/task-1"), ("GET", "/api/validation-tasks/task-1"),
("PUT", "/api/validation/tasks/task-1"), ("PUT", "/api/validation-tasks/task-1"),
("DELETE", "/api/validation/tasks/task-1"), ("DELETE", "/api/validation-tasks/task-1"),
("POST", "/api/validation/tasks/task-1/run"), ("POST", "/api/validation-tasks/task-1/run"),
("GET", "/api/validation/runs"), ("GET", "/api/validation-runs"),
("GET", "/api/validation/runs/run-1"), ("GET", "/api/validation-runs/run-1"),
("DELETE", "/api/validation/runs/run-1"), ("DELETE", "/api/validation-runs/run-1"),
] ]
for method, path in action_endpoints: for method, path in action_endpoints:
response = client.request(method, path) 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. # @BRIEF delete_runs query param defaults to False.
def test_delete_task_runs_default_false(mock_all_deps): def test_delete_task_runs_default_false(mock_all_deps):
mock_all_deps["task_service"].delete_task.return_value = None 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 assert response.status_code == 204
mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=False) mock_all_deps["task_service"].delete_task.assert_called_with("task-1", delete_runs=False)
# #endregion test_delete_task_runs_default_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] # #region create_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Create a new validation task. # @BRIEF Create a new validation task.
# @RELATION CALLS -> [ValidationTaskService.create_task] # @RELATION CALLS -> [ValidationTaskService.create_task]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy] # @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@@ -129,6 +130,7 @@ async def create_task(
# #region list_tasks [C:3] [TYPE Function] # #region list_tasks [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List all validation tasks with pagination and optional filters. # @BRIEF List all validation tasks with pagination and optional filters.
# @RELATION CALLS -> [ValidationTaskService.list_tasks] # @RELATION CALLS -> [ValidationTaskService.list_tasks]
@router.get("", response_model=ValidationTaskListResponse) @router.get("", response_model=ValidationTaskListResponse)
@@ -159,6 +161,7 @@ async def list_tasks(
# #region get_task [C:3] [TYPE Function] # #region get_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Get a validation task by policy ID. # @BRIEF Get a validation task by policy ID.
# @RELATION CALLS -> [ValidationTaskService.get_task] # @RELATION CALLS -> [ValidationTaskService.get_task]
@router.get("/{policy_id}", response_model=ValidationTaskResponse) @router.get("/{policy_id}", response_model=ValidationTaskResponse)
@@ -181,6 +184,7 @@ async def get_task(
# #region update_task [C:3] [TYPE Function] # #region update_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Update a validation task. # @BRIEF Update a validation task.
# @RELATION CALLS -> [ValidationTaskService.update_task] # @RELATION CALLS -> [ValidationTaskService.update_task]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy] # @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@@ -208,6 +212,7 @@ async def update_task(
# #region delete_task [C:3] [TYPE Function] # #region delete_task [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Delete a validation task. # @BRIEF Delete a validation task.
# @RELATION CALLS -> [ValidationTaskService.delete_task] # @RELATION CALLS -> [ValidationTaskService.delete_task]
# @RELATION CALLS -> [SchedulerService.remove_validation_job] # @RELATION CALLS -> [SchedulerService.remove_validation_job]
@@ -234,6 +239,7 @@ async def delete_task(
# #region toggle_task_status [C:3] [TYPE Function] # #region toggle_task_status [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Toggle a validation task's active status. # @BRIEF Toggle a validation task's active status.
# @RELATION CALLS -> [ValidationTaskService.update_task] # @RELATION CALLS -> [ValidationTaskService.update_task]
# @RELATION CALLS -> [SchedulerService.remove_validation_job] # @RELATION CALLS -> [SchedulerService.remove_validation_job]
@@ -270,6 +276,7 @@ async def toggle_task_status(
# #region trigger_run [C:3] [TYPE Function] # #region trigger_run [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Trigger immediate validation for a task. # @BRIEF Trigger immediate validation for a task.
# @RELATION CALLS -> [ValidationTaskService.trigger_run] # @RELATION CALLS -> [ValidationTaskService.trigger_run]
@router.post("/{policy_id}/run", response_model=TriggerRunResponse) @router.post("/{policy_id}/run", response_model=TriggerRunResponse)
@@ -300,6 +307,7 @@ async def trigger_run(
# #region list_all_runs [C:3] [TYPE Function] # #region list_all_runs [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List validation runs across all tasks with optional filters. # @BRIEF List validation runs across all tasks with optional filters.
# @RELATION CALLS -> [ValidationTaskService.list_all_runs] # @RELATION CALLS -> [ValidationTaskService.list_all_runs]
@router.get("/runs/all", response_model=RecordListResponse) @router.get("/runs/all", response_model=RecordListResponse)
@@ -330,6 +338,7 @@ async def list_all_runs(
# #region list_runs [C:3] [TYPE Function] # #region list_runs [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF List run history for a validation task. # @BRIEF List run history for a validation task.
# @RELATION CALLS -> [ValidationTaskService.get_run_history] # @RELATION CALLS -> [ValidationTaskService.get_run_history]
@router.get("/{policy_id}/runs", response_model=RunListResponse) @router.get("/{policy_id}/runs", response_model=RunListResponse)
@@ -356,6 +365,7 @@ async def list_runs(
# #region get_run_detail [C:3] [TYPE Function] # #region get_run_detail [C:3] [TYPE Function]
# @ingroup Api
# @BRIEF Get full run detail with all records for a validation task. # @BRIEF Get full run detail with all records for a validation task.
# @RELATION CALLS -> [ValidationTaskService.get_run_detail] # @RELATION CALLS -> [ValidationTaskService.get_run_detail]
@router.get("/{policy_id}/runs/{run_id}", response_model=RunDetailResponse) @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] # #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. # @BRIEF Parse a Superset dashboard URL and extract dashboard context, native filters, and recovery info.
# @RELATION CALLS -> [SupersetContextExtractor.parse_superset_link] # @RELATION CALLS -> [SupersetContextExtractor.parse_superset_link]
@router.post("/parse-url") @router.post("/parse-url")
@@ -453,6 +464,7 @@ async def parse_superset_url(
# #region legacy_llm_report_redirect [C:2] [TYPE Function] # #region legacy_llm_report_redirect [C:2] [TYPE Function]
# @ingroup Api
# @BRIEF Legacy redirect: /reports/llm/{taskId} → /validation-tasks/{policy_id}/runs/{run_id} # @BRIEF Legacy redirect: /reports/llm/{taskId} → /validation-tasks/{policy_id}/runs/{run_id}
# @RELATION CALLS -> [ValidationRun] # @RELATION CALLS -> [ValidationRun]
@router.get("/reports/llm/{task_id}") @router.get("/reports/llm/{task_id}")

View File

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

View File

@@ -12,7 +12,7 @@ import requests
from src.core.config_models import Environment from src.core.config_models import Environment
from src.core.superset_client import SupersetClient from src.core.superset_client import SupersetClient
from src.core.utils.async_network import AsyncAPIClient 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] # #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["url_params"] == {"country": "DE"}
assert legacy_form_data["result_type"] == "query" assert legacy_form_data["result_type"] == "query"
# #endregion test_build_dataset_preview_legacy_form_data_preserves_native_filter_clauses # #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] # #region test_async_network_404_mapping_keeps_non_dashboard_endpoints_generic [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests # @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Async network client should reserve dashboard-not-found translation for dashboard endpoints only. # @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] # #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. # @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. # @DEPRECATED 2026-06-04 — class kept as thin compat wrapper. New code should import SupersetClient directly.
# @REPLACED_BY -> [SupersetClient] # @REPLACED_BY -> [SupersetClient]
@@ -13,6 +14,7 @@ from .utils.async_network import AsyncAPIClient
# #region AsyncSupersetClient [C:3] [TYPE Class] # #region AsyncSupersetClient [C:3] [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Backward-compatible alias for the async-migrated SupersetClient. # @BRIEF Backward-compatible alias for the async-migrated SupersetClient.
# @RELATION INHERITS -> [SupersetClient] # @RELATION INHERITS -> [SupersetClient]
# @RELATION DEPENDS_ON -> [AsyncAPIClient] # @RELATION DEPENDS_ON -> [AsyncAPIClient]
@@ -25,6 +27,7 @@ class AsyncSupersetClient(SupersetClient):
""" """
# #region AsyncSupersetClientInit [TYPE Function] [C:2] # #region AsyncSupersetClientInit [TYPE Function] [C:2]
# @ingroup Core
# @PURPOSE: Initialize async Superset client — delegates to SupersetClient. # @PURPOSE: Initialize async Superset client — delegates to SupersetClient.
# @PRE env is valid Environment instance. # @PRE env is valid Environment instance.
# @POST Client uses AsyncAPIClient transport. # @POST Client uses AsyncAPIClient transport.
@@ -35,6 +38,7 @@ class AsyncSupersetClient(SupersetClient):
# #endregion AsyncSupersetClientInit # #endregion AsyncSupersetClientInit
# #region AsyncSupersetClientClose [TYPE Function] [C:2] # #region AsyncSupersetClientClose [TYPE Function] [C:2]
# @ingroup Core
# @PURPOSE: Close async transport resources. # @PURPOSE: Close async transport resources.
# @POST Underlying AsyncAPIClient is closed. # @POST Underlying AsyncAPIClient is closed.
# @SIDE_EFFECT Closes network sockets. # @SIDE_EFFECT Closes network sockets.

View File

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

View File

@@ -1,4 +1,5 @@
# #region APIKeyUtilities [C:2] [TYPE Module] [SEMANTICS auth, api_key, crypto, generation] # #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. # @BRIEF API key generation and hashing utilities for service-to-service authentication.
# @LAYER Core # @LAYER Core
# @RELATION DEPENDS_ON -> [EXT:Python:hashlib] # @RELATION DEPENDS_ON -> [EXT:Python:hashlib]
@@ -11,6 +12,7 @@ import secrets
# #region generate_api_key [C:2] [TYPE Function] # #region generate_api_key [C:2] [TYPE Function]
# @ingroup Auth
# @BRIEF Generate a new API key in ssk_ format with SHA-256 hash. # @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. # @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. # @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] # #region AuthConfigModule [C:2] [TYPE Module] [SEMANTICS pydantic, auth, auth-config, config]
# @defgroup Auth Module group.
# #
# @BRIEF Centralized configuration for authentication and authorization. # @BRIEF Centralized configuration for authentication and authorization.
# @LAYER Core # @LAYER Core
@@ -17,6 +18,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
# #region AuthConfig [TYPE Class] # #region AuthConfig [TYPE Class]
# @defgroup Auth Module group.
# @BRIEF Holds authentication-related settings. # @BRIEF Holds authentication-related settings.
# @PRE Environment variables may be provided via .env file. # @PRE Environment variables may be provided via .env file.
# @POST Returns a configuration object with validated settings. # @POST Returns a configuration object with validated settings.
@@ -65,6 +67,7 @@ class AuthConfig(BaseSettings):
# #endregion AuthConfig # #endregion AuthConfig
# #region auth_config [TYPE Variable] # #region auth_config [TYPE Variable]
# @ingroup Auth
# @BRIEF Singleton instance of AuthConfig. # @BRIEF Singleton instance of AuthConfig.
# @RELATION DEPENDS_ON -> AuthConfig # @RELATION DEPENDS_ON -> AuthConfig
auth_config = AuthConfig() auth_config = AuthConfig()

View File

@@ -1,16 +1,15 @@
# #region AuthJwtModule [C:5] [TYPE Module] [SEMANTICS auth, validate, jwt, token] # #region Auth.Jwt [C:5] [TYPE Module] [SEMANTICS auth,jwt,token,security]
# # @defgroup Auth JWT token generation, validation, and revocation.
# @BRIEF JWT token generation and validation logic with server-side revocation.
# @LAYER Core # @LAYER Core
# @RELATION DEPENDS_ON -> [auth_config] # @RELATION DEPENDS_ON -> [Auth.Config]
# @RELATION DEPENDS_ON -> [TokenBlacklist] # @RELATION DEPENDS_ON -> [Auth.TokenBlacklist]
# # @INVARIANT Tokens must include aud, iss, iat, jti, exp, and sub claims.
# @INVARIANT Tokens must include aud, iss, iat, jti, exp, and sub claims. # @INVARIANT Revoked tokens are rejected even if not expired.
# Revoked tokens are rejected even if not expired. # @PRE JWT secret configured in environment.
# @PRE JWT secret configured in environment # @POST Token encode/decode/revoke functions exported.
# @POST Token encode/decode/revoke functions exported # @DATA_CONTRACT TokenPayload -> JWT string
# @SIDE_EFFECT None # @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).
# @DATA_CONTRACT TokenPayload -> JWT string # @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 from datetime import UTC, datetime, timedelta
import hashlib import hashlib
@@ -24,11 +23,13 @@ from ..logger import belief_scope
from .config import auth_config 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. # @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). # @PRE data dict contains 'sub' (user_id) and optional 'scopes'.
# @POST Returns a signed JWT string with unique jti for revocation. # @POST Returns a signed JWT string with unique jti for revocation.
# @RELATION DEPENDS_ON -> [auth_config] # @SIDE_EFFECT None (pure function).
# @RELATION DEPENDS_ON -> [Auth.Config]
# #
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str: def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
with belief_scope("create_access_token"): 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 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. # @BRIEF Decodes and validates a JWT token — verifies aud, iss, exp, and blacklist.
# @PRE token is a signed JWT string. # @PRE token is a signed JWT string.
# @POST Returns the decoded payload if valid. Raises JWTError if invalid. # @POST Returns decoded payload if valid. Raises JWTError if invalid/revoked.
# @RELATION DEPENDS_ON -> [auth_config] # @RELATION DEPENDS_ON -> [Auth.Config]
# @RELATION CALLS -> [Auth.Jwt.IsTokenBlacklisted]
# #
def decode_token(token: str) -> dict: def decode_token(token: str) -> dict:
with belief_scope("decode_token"): with belief_scope("decode_token"):
@@ -81,39 +84,41 @@ def decode_token(token: str) -> dict:
return payload 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. # @BRIEF SHA-256 hash of a token string for blacklist lookup.
# @PRE token is a non-empty JWT string. # @PRE token is a non-empty JWT string.
# @POST Returns 64-character hex digest. # @POST Returns 64-character hex digest.
def _hash_token(token: str) -> str: def _hash_token(token: str) -> str:
return hashlib.sha256(token.encode()).hexdigest() return hashlib.sha256(token.encode()).hexdigest()
# #endregion Auth.Jwt._HashToken
# #endregion _hash_token
# #region _prune_blacklist [TYPE Function] # #region Auth.Jwt._PruneBlacklist [C:2] [TYPE Function]
# @BRIEF Delete expired entries from the token blacklist. # @BRIEF Delete expired entries from the token blacklist.
# @PRE db is a valid SQLAlchemy session. # @PRE db is a valid SQLAlchemy session.
# @POST Expired blacklist rows are removed. # @POST Expired blacklist rows are removed.
# @SIDE_EFFECT Mutates token_blacklist table.
def _prune_blacklist(db: Session) -> None: def _prune_blacklist(db: Session) -> None:
now = datetime.now(UTC) now = datetime.now(UTC)
db.query(TokenBlacklist).filter(TokenBlacklist.expires_at < now).delete() db.query(TokenBlacklist).filter(TokenBlacklist.expires_at < now).delete()
db.commit() db.commit()
# #endregion Auth.Jwt._PruneBlacklist
# #endregion _prune_blacklist
# #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. # @BRIEF Revoke a JWT token by adding its hash to the blacklist.
# @PRE token was issued by this service and hasn't expired. # @PRE token was issued by this service and hasn't expired.
# @POST Token is blacklisted and subsequent decode_token calls will reject it. # @POST Token is blacklisted; subsequent decode attempts will reject it.
# @SIDE_EFFECT Writes to token_blacklist table; prunes expired entries. # @SIDE_EFFECT Writes to token_blacklist table; prunes expired entries.
# @RELATION DEPENDS_ON -> [TokenBlacklist] # @RELATION DEPENDS_ON -> [Auth.TokenBlacklist]
# @RELATION CALLS -> [Auth.Jwt.HashToken]
# @TEST_EDGE: already_expired -> skips blacklisting
def blacklist_token(token: str, db: Session) -> None: def blacklist_token(token: str, db: Session) -> None:
with belief_scope("blacklist_token"): with belief_scope("blacklist_token"):
_prune_blacklist(db) _prune_blacklist(db)
@@ -145,14 +150,15 @@ def blacklist_token(token: str, db: Session) -> None:
db.commit() db.commit()
# #endregion blacklist_token # #endregion Auth.Jwt.BlacklistToken
# #region is_token_blacklisted [TYPE Function] # #region Auth.Jwt.IsTokenBlacklisted [C:2] [TYPE Function] [SEMANTICS auth,jwt,revoke]
# @BRIEF Check if a JWT token has been revoked. # @ingroup Auth
# @PRE db is a valid SQLAlchemy session. # @BRIEF Check if a JWT token hash exists in the blacklist.
# @POST Returns True if token is blacklisted, False otherwise. # @PRE db is a valid SQLAlchemy session.
# @RELATION DEPENDS_ON -> [TokenBlacklist] # @POST Returns True if token is blacklisted, False otherwise.
# @RELATION DEPENDS_ON -> [Auth.TokenBlacklist]
def is_token_blacklisted(token: str, db: Session) -> bool: def is_token_blacklisted(token: str, db: Session) -> bool:
with belief_scope("is_token_blacklisted"): with belief_scope("is_token_blacklisted"):
try: 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 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] # #region AuthLoggerModule [C:5] [TYPE Module] [SEMANTICS auth, audit, logging]
# @defgroup Auth Module group.
# #
# @BRIEF Structured auth logging module for audit trail generation. # @BRIEF Structured auth logging module for audit trail generation.
# @LAYER Core # @LAYER Core
@@ -41,6 +42,7 @@ def _mask_details(details: dict[str, Any] | None) -> dict[str, Any] | None:
# #region log_security_event [TYPE Function] # #region log_security_event [TYPE Function]
# @ingroup Auth
# @BRIEF Logs a security-related event for audit trails. # @BRIEF Logs a security-related event for audit trails.
# @PRE event_type and username are strings. # @PRE event_type and username are strings.
# @POST Security event is written to the application log via %s-formatting to prevent log injection. # @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] # #region AuthOauthModule [C:2] [TYPE Module] [SEMANTICS auth, oauth, oidc, adfs, authlib]
# @defgroup Auth Module group.
# #
# @BRIEF ADFS OIDC configuration and client using Authlib. # @BRIEF ADFS OIDC configuration and client using Authlib.
# @LAYER Core # @LAYER Core
@@ -12,12 +13,14 @@ from authlib.integrations.starlette_client import OAuth
from .config import auth_config from .config import auth_config
# #region oauth [TYPE Variable] # #region oauth [TYPE Variable]
# @ingroup Auth
# @BRIEF Global Authlib OAuth registry. # @BRIEF Global Authlib OAuth registry.
# @RELATION DEPENDS_ON -> [EXT:Library:OAuth] # @RELATION DEPENDS_ON -> [EXT:Library:OAuth]
oauth = OAuth() oauth = OAuth()
# #endregion oauth # #endregion oauth
# #region register_adfs [TYPE Function] # #region register_adfs [TYPE Function]
# @ingroup Auth
# @BRIEF Registers the ADFS OIDC client. # @BRIEF Registers the ADFS OIDC client.
# @PRE ADFS configuration is provided in auth_config. # @PRE ADFS configuration is provided in auth_config.
# @POST ADFS client is registered in oauth registry. # @POST ADFS client is registered in oauth registry.
@@ -37,6 +40,7 @@ def register_adfs():
# #endregion register_adfs # #endregion register_adfs
# #region is_adfs_configured [TYPE Function] # #region is_adfs_configured [TYPE Function]
# @ingroup Auth
# @BRIEF Checks if ADFS is properly configured. # @BRIEF Checks if ADFS is properly configured.
# @PRE None. # @PRE None.
# @POST Returns True if ADFS client is registered, False otherwise. # @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] # #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. # @BRIEF Data access layer for authentication and user preference entities.
# @LAYER Domain # @LAYER Domain
# @RELATION DEPENDS_ON -> [AuthModels] # @RELATION DEPENDS_ON -> [AuthModels]
@@ -18,6 +19,7 @@ from ..logger import belief_scope, logger
# #region AuthRepository [TYPE Class] # #region AuthRepository [TYPE Class]
# @defgroup Auth Module group.
# @BRIEF Provides low-level CRUD operations for identity and authorization records. # @BRIEF Provides low-level CRUD operations for identity and authorization records.
# @PRE Database session is bound. # @PRE Database session is bound.
# @POST Entity instances returned safely. # @POST Entity instances returned safely.
@@ -27,6 +29,7 @@ class AuthRepository:
def __init__(self, db: Session): def __init__(self, db: Session):
self.db = db self.db = db
# #region get_user_by_id [TYPE Function] # #region get_user_by_id [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve user by UUID. # @PURPOSE: Retrieve user by UUID.
# @PRE user_id is a valid UUID string. # @PRE user_id is a valid UUID string.
# @POST Returns User object if found, else None. # @POST Returns User object if found, else None.
@@ -39,6 +42,7 @@ class AuthRepository:
return result return result
# #endregion get_user_by_id # #endregion get_user_by_id
# #region get_user_by_username [TYPE Function] # #region get_user_by_username [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve user by username. # @PURPOSE: Retrieve user by username.
# @PRE username is a non-empty string. # @PRE username is a non-empty string.
# @POST Returns User object if found, else None. # @POST Returns User object if found, else None.
@@ -51,6 +55,7 @@ class AuthRepository:
return result return result
# #endregion get_user_by_username # #endregion get_user_by_username
# #region get_role_by_id [TYPE Function] # #region get_role_by_id [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve role by UUID with permissions preloaded. # @PURPOSE: Retrieve role by UUID with permissions preloaded.
# @RELATION DEPENDS_ON -> [Role] # @RELATION DEPENDS_ON -> [Role]
# @RELATION DEPENDS_ON -> [Permission] # @RELATION DEPENDS_ON -> [Permission]
@@ -64,6 +69,7 @@ class AuthRepository:
) )
# #endregion get_role_by_id # #endregion get_role_by_id
# #region get_role_by_name [TYPE Function] # #region get_role_by_name [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve role by unique name. # @PURPOSE: Retrieve role by unique name.
# @RELATION DEPENDS_ON -> [Role] # @RELATION DEPENDS_ON -> [Role]
def get_role_by_name(self, name: str) -> Role | None: 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() return self.db.query(Role).filter(Role.name == name).first()
# #endregion get_role_by_name # #endregion get_role_by_name
# #region get_permission_by_id [TYPE Function] # #region get_permission_by_id [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve permission by UUID. # @PURPOSE: Retrieve permission by UUID.
# @RELATION DEPENDS_ON -> [Permission] # @RELATION DEPENDS_ON -> [Permission]
def get_permission_by_id(self, permission_id: str) -> Permission | None: def get_permission_by_id(self, permission_id: str) -> Permission | None:
@@ -80,6 +87,7 @@ class AuthRepository:
) )
# #endregion get_permission_by_id # #endregion get_permission_by_id
# #region get_permission_by_resource_action [TYPE Function] # #region get_permission_by_resource_action [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve permission by resource and action tuple. # @PURPOSE: Retrieve permission by resource and action tuple.
# @RELATION DEPENDS_ON -> [Permission] # @RELATION DEPENDS_ON -> [Permission]
def get_permission_by_resource_action( def get_permission_by_resource_action(
@@ -93,6 +101,7 @@ class AuthRepository:
) )
# #endregion get_permission_by_resource_action # #endregion get_permission_by_resource_action
# #region list_permissions [TYPE Function] # #region list_permissions [TYPE Function]
# @ingroup Auth
# @PURPOSE: List all system permissions. # @PURPOSE: List all system permissions.
# @RELATION DEPENDS_ON -> [Permission] # @RELATION DEPENDS_ON -> [Permission]
def list_permissions(self) -> list[Permission]: def list_permissions(self) -> list[Permission]:
@@ -100,6 +109,7 @@ class AuthRepository:
return self.db.query(Permission).all() return self.db.query(Permission).all()
# #endregion list_permissions # #endregion list_permissions
# #region get_user_dashboard_preference [TYPE Function] # #region get_user_dashboard_preference [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve dashboard filters/preferences for a user. # @PURPOSE: Retrieve dashboard filters/preferences for a user.
# @RELATION DEPENDS_ON -> [UserDashboardPreference] # @RELATION DEPENDS_ON -> [UserDashboardPreference]
def get_user_dashboard_preference( def get_user_dashboard_preference(
@@ -113,6 +123,7 @@ class AuthRepository:
) )
# #endregion get_user_dashboard_preference # #endregion get_user_dashboard_preference
# #region get_roles_by_ad_groups [TYPE Function] # #region get_roles_by_ad_groups [TYPE Function]
# @ingroup Auth
# @PURPOSE: Retrieve roles that match a list of AD group names. # @PURPOSE: Retrieve roles that match a list of AD group names.
# @PRE groups is a list of strings representing AD group identifiers. # @PRE groups is a list of strings representing AD group identifiers.
# @POST Returns a list of Role objects mapped to the provided AD groups. # @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] # #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. # @BRIEF Utility for password hashing and verification using Passlib.
# @LAYER Core # @LAYER Core
@@ -10,6 +11,7 @@ import bcrypt
# #region verify_password [TYPE Function] # #region verify_password [TYPE Function]
# @ingroup Auth
# @BRIEF Verifies a plain password against a hashed password. # @BRIEF Verifies a plain password against a hashed password.
# @PRE plain_password is a string, hashed_password is a bcrypt hash. # @PRE plain_password is a string, hashed_password is a bcrypt hash.
# @POST Returns True if password matches, False otherwise. # @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 # #endregion verify_password
# #region get_password_hash [TYPE Function] # #region get_password_hash [TYPE Function]
# @ingroup Auth
# @BRIEF Generates a bcrypt hash for a plain password. # @BRIEF Generates a bcrypt hash for a plain password.
# @PRE password is a string. # @PRE password is a string.
# @POST Returns a secure bcrypt hash 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] # #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. # @BRIEF Manages application configuration persistence in DB with one-time migration from legacy JSON.
# @LAYER Domain # @LAYER Domain
@@ -31,6 +32,7 @@ from .logger import belief_scope, configure_logger, logger
# #region ConfigManager [C:5] [TYPE Class] # #region ConfigManager [C:5] [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Handles application configuration load, validation, mutation, and persistence lifecycle. # @BRIEF Handles application configuration load, validation, mutation, and persistence lifecycle.
# @PRE Database is accessible and AppConfigRecord schema is loaded. # @PRE Database is accessible and AppConfigRecord schema is loaded.
# @POST Configuration state is synchronized between memory and database. # @POST Configuration state is synchronized between memory and database.
@@ -446,6 +448,7 @@ class ConfigManager:
db.close() db.close()
# #endregion _save_config_to_db # #endregion _save_config_to_db
# #region save [TYPE Function] # #region save [TYPE Function]
# @ingroup Core
# @PURPOSE: Persist current in-memory configuration state. # @PURPOSE: Persist current in-memory configuration state.
def save(self) -> None: def save(self) -> None:
with belief_scope("ConfigManager.save"): with belief_scope("ConfigManager.save"):
@@ -453,18 +456,21 @@ class ConfigManager:
self._save_config_to_db(self.config) self._save_config_to_db(self.config)
# #endregion save # #endregion save
# #region get_config [TYPE Function] # #region get_config [TYPE Function]
# @ingroup Core
# @PURPOSE: Return current in-memory configuration snapshot. # @PURPOSE: Return current in-memory configuration snapshot.
def get_config(self) -> AppConfig: def get_config(self) -> AppConfig:
with belief_scope("ConfigManager.get_config"): with belief_scope("ConfigManager.get_config"):
return self.config return self.config
# #endregion get_config # #endregion get_config
# #region get_payload [TYPE Function] # #region get_payload [TYPE Function]
# @ingroup Core
# @PURPOSE: Return full persisted payload including sections outside typed AppConfig schema. # @PURPOSE: Return full persisted payload including sections outside typed AppConfig schema.
def get_payload(self) -> dict[str, Any]: def get_payload(self) -> dict[str, Any]:
with belief_scope("ConfigManager.get_payload"): with belief_scope("ConfigManager.get_payload"):
return self._sync_raw_payload_from_config() return self._sync_raw_payload_from_config()
# #endregion get_payload # #endregion get_payload
# #region save_config [TYPE Function] # #region save_config [TYPE Function]
# @ingroup Core
# @PURPOSE: Persist configuration provided either as typed AppConfig or raw payload dict. # @PURPOSE: Persist configuration provided either as typed AppConfig or raw payload dict.
def save_config(self, config: Any) -> AppConfig: def save_config(self, config: Any) -> AppConfig:
with belief_scope("ConfigManager.save_config"): with belief_scope("ConfigManager.save_config"):
@@ -496,6 +502,7 @@ class ConfigManager:
raise TypeError("config must be AppConfig or dict") raise TypeError("config must be AppConfig or dict")
# #endregion save_config # #endregion save_config
# #region update_global_settings [TYPE Function] # #region update_global_settings [TYPE Function]
# @ingroup Core
# @PURPOSE: Replace global settings and persist the resulting configuration. # @PURPOSE: Replace global settings and persist the resulting configuration.
def update_global_settings(self, settings: GlobalSettings) -> AppConfig: def update_global_settings(self, settings: GlobalSettings) -> AppConfig:
with belief_scope("ConfigManager.update_global_settings"): with belief_scope("ConfigManager.update_global_settings"):
@@ -505,6 +512,7 @@ class ConfigManager:
return self.config return self.config
# #endregion update_global_settings # #endregion update_global_settings
# #region validate_path [TYPE Function] # #region validate_path [TYPE Function]
# @ingroup Core
# @PURPOSE: Validate that path exists and is writable, creating it when absent. # @PURPOSE: Validate that path exists and is writable, creating it when absent.
def validate_path(self, path: str) -> tuple[bool, str]: def validate_path(self, path: str) -> tuple[bool, str]:
with belief_scope("ConfigManager.validate_path", f"path={path}"): with belief_scope("ConfigManager.validate_path", f"path={path}"):
@@ -528,18 +536,21 @@ class ConfigManager:
return False, str(exc) return False, str(exc)
# #endregion validate_path # #endregion validate_path
# #region get_environments [TYPE Function] # #region get_environments [TYPE Function]
# @ingroup Core
# @PURPOSE: Return all configured environments. # @PURPOSE: Return all configured environments.
def get_environments(self) -> list[Environment]: def get_environments(self) -> list[Environment]:
with belief_scope("ConfigManager.get_environments"): with belief_scope("ConfigManager.get_environments"):
return list(self.config.environments) return list(self.config.environments)
# #endregion get_environments # #endregion get_environments
# #region has_environments [TYPE Function] # #region has_environments [TYPE Function]
# @ingroup Core
# @PURPOSE: Check whether at least one environment exists in configuration. # @PURPOSE: Check whether at least one environment exists in configuration.
def has_environments(self) -> bool: def has_environments(self) -> bool:
with belief_scope("ConfigManager.has_environments"): with belief_scope("ConfigManager.has_environments"):
return len(self.config.environments) > 0 return len(self.config.environments) > 0
# #endregion has_environments # #endregion has_environments
# #region get_environment [TYPE Function] # #region get_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Resolve a configured environment by identifier. # @PURPOSE: Resolve a configured environment by identifier.
def get_environment(self, env_id: str) -> Environment | None: def get_environment(self, env_id: str) -> Environment | None:
with belief_scope("ConfigManager.get_environment", f"env_id={env_id}"): with belief_scope("ConfigManager.get_environment", f"env_id={env_id}"):
@@ -552,6 +563,7 @@ class ConfigManager:
return None return None
# #endregion get_environment # #endregion get_environment
# #region add_environment [TYPE Function] # #region add_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Upsert environment by id into configuration and persist. # @PURPOSE: Upsert environment by id into configuration and persist.
def add_environment(self, env: Environment) -> AppConfig: def add_environment(self, env: Environment) -> AppConfig:
with belief_scope("ConfigManager.add_environment", f"env_id={env.id}"): with belief_scope("ConfigManager.add_environment", f"env_id={env.id}"):
@@ -583,6 +595,7 @@ class ConfigManager:
return self.config return self.config
# #endregion add_environment # #endregion add_environment
# #region update_environment [TYPE Function] # #region update_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Update existing environment by id and preserve masked password placeholder behavior. # @PURPOSE: Update existing environment by id and preserve masked password placeholder behavior.
def update_environment(self, env_id: str, env: Environment) -> bool: def update_environment(self, env_id: str, env: Environment) -> bool:
with belief_scope("ConfigManager.update_environment", f"env_id={env_id}"): with belief_scope("ConfigManager.update_environment", f"env_id={env_id}"):
@@ -608,6 +621,7 @@ class ConfigManager:
return False return False
# #endregion update_environment # #endregion update_environment
# #region delete_environment [TYPE Function] # #region delete_environment [TYPE Function]
# @ingroup Core
# @PURPOSE: Delete environment by id and persist when deletion occurs. # @PURPOSE: Delete environment by id and persist when deletion occurs.
def delete_environment(self, env_id: str) -> bool: def delete_environment(self, env_id: str) -> bool:
with belief_scope("ConfigManager.delete_environment", f"env_id={env_id}"): with belief_scope("ConfigManager.delete_environment", f"env_id={env_id}"):

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