semantics: complete DEF-to-region migration, fix regressions
- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# #region CleanReleaseCliScript [C:3] [TYPE Module] [SEMANTICS cli, clean-release, candidate, artifacts, manifest]
|
||||
# @BRIEF Provide headless CLI commands for candidate registration, artifact import and manifest build.
|
||||
# @LAYER Scripts
|
||||
# @RELATION CALLS -> [ComplianceOrchestrator]
|
||||
# @LAYER: Scripts
|
||||
# @RELATION CALLS -> ComplianceOrchestrator
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -102,8 +102,8 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
# #region run_candidate_register [TYPE Function]
|
||||
# @BRIEF Register candidate in repository via CLI command.
|
||||
# @PRE Candidate ID must be unique.
|
||||
# @POST Candidate is persisted in DRAFT status.
|
||||
# @PRE: Candidate ID must be unique.
|
||||
# @POST: Candidate is persisted in DRAFT status.
|
||||
def run_candidate_register(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -131,8 +131,8 @@ def run_candidate_register(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_artifact_import [TYPE Function]
|
||||
# @BRIEF Import single artifact for existing candidate.
|
||||
# @PRE Candidate must exist.
|
||||
# @POST Artifact is persisted for candidate.
|
||||
# @PRE: Candidate must exist.
|
||||
# @POST: Artifact is persisted for candidate.
|
||||
def run_artifact_import(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -164,8 +164,8 @@ def run_artifact_import(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_manifest_build [TYPE Function]
|
||||
# @BRIEF Build immutable manifest snapshot for candidate.
|
||||
# @PRE Candidate must exist.
|
||||
# @POST New manifest version is persisted.
|
||||
# @PRE: Candidate must exist.
|
||||
# @POST: New manifest version is persisted.
|
||||
def run_manifest_build(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
from ..services.clean_release.manifest_service import build_manifest_snapshot
|
||||
@@ -198,8 +198,8 @@ def run_manifest_build(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_compliance_run [TYPE Function]
|
||||
# @BRIEF Execute compliance run for candidate with optional manifest fallback.
|
||||
# @PRE Candidate exists and trusted snapshots are configured.
|
||||
# @POST Returns run payload and exit code 0 on success.
|
||||
# @PRE: Candidate exists and trusted snapshots are configured.
|
||||
# @POST: Returns run payload and exit code 0 on success.
|
||||
def run_compliance_run(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository, get_config_manager
|
||||
|
||||
@@ -237,8 +237,8 @@ def run_compliance_run(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_compliance_status [TYPE Function]
|
||||
# @BRIEF Read run status by run id.
|
||||
# @PRE Run exists.
|
||||
# @POST Returns run status payload.
|
||||
# @PRE: Run exists.
|
||||
# @POST: Returns run status payload.
|
||||
def run_compliance_status(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -269,8 +269,8 @@ def run_compliance_status(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region _to_payload [TYPE Function]
|
||||
# @BRIEF Serialize domain models for CLI JSON output across SQLAlchemy/Pydantic variants.
|
||||
# @PRE value is serializable model or primitive object.
|
||||
# @POST Returns dictionary payload without mutating value.
|
||||
# @PRE: value is serializable model or primitive object.
|
||||
# @POST: Returns dictionary payload without mutating value.
|
||||
def _to_payload(value: Any) -> Dict[str, Any]:
|
||||
def _normalize(raw: Any) -> Any:
|
||||
if isinstance(raw, datetime):
|
||||
@@ -299,8 +299,8 @@ def _to_payload(value: Any) -> Dict[str, Any]:
|
||||
|
||||
# #region run_compliance_report [TYPE Function]
|
||||
# @BRIEF Read immutable report by run id.
|
||||
# @PRE Run and report exist.
|
||||
# @POST Returns report payload.
|
||||
# @PRE: Run and report exist.
|
||||
# @POST: Returns report payload.
|
||||
def run_compliance_report(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -326,8 +326,8 @@ def run_compliance_report(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_compliance_violations [TYPE Function]
|
||||
# @BRIEF Read run violations by run id.
|
||||
# @PRE Run exists.
|
||||
# @POST Returns violations payload.
|
||||
# @PRE: Run exists.
|
||||
# @POST: Returns violations payload.
|
||||
def run_compliance_violations(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -351,8 +351,8 @@ def run_compliance_violations(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_approve [TYPE Function]
|
||||
# @BRIEF Approve candidate based on immutable PASSED report.
|
||||
# @PRE Candidate and report exist; report is PASSED.
|
||||
# @POST Persists APPROVED decision and returns success payload.
|
||||
# @PRE: Candidate and report exist; report is PASSED.
|
||||
# @POST: Persists APPROVED decision and returns success payload.
|
||||
def run_approve(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -382,8 +382,8 @@ def run_approve(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_reject [TYPE Function]
|
||||
# @BRIEF Reject candidate without mutating compliance evidence.
|
||||
# @PRE Candidate and report exist.
|
||||
# @POST Persists REJECTED decision and returns success payload.
|
||||
# @PRE: Candidate and report exist.
|
||||
# @POST: Persists REJECTED decision and returns success payload.
|
||||
def run_reject(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -413,8 +413,8 @@ def run_reject(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_publish [TYPE Function]
|
||||
# @BRIEF Publish approved candidate to target channel.
|
||||
# @PRE Candidate is approved and report belongs to candidate.
|
||||
# @POST Appends ACTIVE publication record and returns payload.
|
||||
# @PRE: Candidate is approved and report belongs to candidate.
|
||||
# @POST: Appends ACTIVE publication record and returns payload.
|
||||
def run_publish(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -441,8 +441,8 @@ def run_publish(args: argparse.Namespace) -> int:
|
||||
|
||||
# #region run_revoke [TYPE Function]
|
||||
# @BRIEF Revoke active publication record.
|
||||
# @PRE Publication id exists and is ACTIVE.
|
||||
# @POST Publication record status becomes REVOKED.
|
||||
# @PRE: Publication id exists and is ACTIVE.
|
||||
# @POST: Publication record status becomes REVOKED.
|
||||
def run_revoke(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# #region CleanReleaseTuiScript [C:3] [TYPE Module] [SEMANTICS clean-release, tui, ncurses, interactive-validator]
|
||||
# @BRIEF Interactive terminal interface for Enterprise Clean Release compliance validation.
|
||||
# @LAYER UI
|
||||
# @INVARIANT TUI refuses startup in non-TTY environments; headless flow is CLI/API only.
|
||||
# @LAYER: UI
|
||||
# @RELATION DEPENDS_ON -> [ComplianceExecutionService]
|
||||
# @RELATION DEPENDS_ON -> [CleanReleaseRepository]
|
||||
# @INVARIANT: TUI refuses startup in non-TTY environments; headless flow is CLI/API only.
|
||||
|
||||
import curses
|
||||
import json
|
||||
@@ -46,8 +46,8 @@ from src.services.clean_release.repository import CleanReleaseRepository
|
||||
|
||||
# #region TuiFacadeAdapter [TYPE Class]
|
||||
# @BRIEF Thin TUI adapter that routes business mutations through application services.
|
||||
# @PRE repository contains candidate and trusted policy/registry snapshots for execution.
|
||||
# @POST Business actions return service results/errors without direct TUI-owned mutations.
|
||||
# @PRE: repository contains candidate and trusted policy/registry snapshots for execution.
|
||||
# @POST: Business actions return service results/errors without direct TUI-owned mutations.
|
||||
class TuiFacadeAdapter:
|
||||
def __init__(self, repository: CleanReleaseRepository):
|
||||
self.repository = repository
|
||||
@@ -506,10 +506,10 @@ class CleanReleaseTUI:
|
||||
self.stdscr.addstr(max_y - 1, 0, footer_text[:max_x])
|
||||
self.stdscr.attroff(curses.color_pair(1))
|
||||
|
||||
# #region run_checks [TYPE Function]
|
||||
# @BRIEF Execute compliance run via facade adapter and update UI state.
|
||||
# @PRE Candidate and policy snapshots are present in repository.
|
||||
# @POST UI reflects final run/report/violation state from service result.
|
||||
# [DEF:run_checks:Function]
|
||||
# @PURPOSE: Execute compliance run via facade adapter and update UI state.
|
||||
# @PRE: Candidate and policy snapshots are present in repository.
|
||||
# @POST: UI reflects final run/report/violation state from service result.
|
||||
def run_checks(self):
|
||||
self.status = "RUNNING"
|
||||
self.report_id = None
|
||||
@@ -550,7 +550,7 @@ class CleanReleaseTUI:
|
||||
self.refresh_overview()
|
||||
self.refresh_screen()
|
||||
|
||||
# #endregion run_checks
|
||||
# [/DEF:run_checks:Function]
|
||||
|
||||
def build_manifest(self):
|
||||
try:
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# #region CreateAdminScript [C:3] [TYPE Module] [SEMANTICS admin, setup, user, auth, cli]
|
||||
#
|
||||
# @BRIEF CLI tool for creating the initial admin user.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Admin user must have the "Admin" role.
|
||||
# @LAYER: Scripts
|
||||
# @RELATION USES -> [AuthSecurityModule]
|
||||
# @RELATION USES -> [DatabaseModule]
|
||||
# @RELATION USES -> [AuthModels]
|
||||
#
|
||||
#
|
||||
# @INVARIANT: Admin user must have the "Admin" role.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
@@ -20,17 +19,13 @@ from src.core.database import AuthSessionLocal, init_db
|
||||
from src.core.auth.security import get_password_hash
|
||||
from src.models.auth import User, Role
|
||||
from src.core.logger import logger, belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# #region create_admin [TYPE Function]
|
||||
# @BRIEF Creates an admin user and necessary roles/permissions.
|
||||
# @PRE username and password provided via CLI.
|
||||
# @POST Admin user exists in auth.db.
|
||||
# @PRE: username and password provided via CLI.
|
||||
# @POST: Admin user exists in auth.db.
|
||||
#
|
||||
# @PARAM: username (str) - Admin username.
|
||||
# @PARAM: password (str) - Admin password.
|
||||
# @PARAM: email (str | None) - Optional admin email.
|
||||
def create_admin(username, password, email=None):
|
||||
with belief_scope("create_admin"):
|
||||
db = AuthSessionLocal()
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# #region InitAuthDbScript [C:2] [TYPE Module] [SEMANTICS setup, database, auth, migration]
|
||||
#
|
||||
# @BRIEF Initializes the auth database and creates the necessary tables.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Safe to run multiple times (idempotent).
|
||||
# @RELATION CALLS -> [init_db]
|
||||
# @RELATION CALLS -> [ensure_encryption_key]
|
||||
# @RELATION CALLS -> [seed_permissions]
|
||||
#
|
||||
# @LAYER: Scripts
|
||||
# @RELATION CALLS -> init_db
|
||||
# @RELATION CALLS -> ensure_encryption_key
|
||||
# @RELATION CALLS -> seed_permissions
|
||||
#
|
||||
# @INVARIANT: Safe to run multiple times (idempotent).
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -19,15 +18,14 @@ from src.core.database import init_db
|
||||
from src.core.encryption_key import ensure_encryption_key
|
||||
from src.core.logger import logger, belief_scope
|
||||
from src.scripts.seed_permissions import seed_permissions
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# #region run_init [C:3] [TYPE Function]
|
||||
# @BRIEF Main entry point for the initialization script.
|
||||
# @POST auth.db is initialized with the correct schema and seeded permissions.
|
||||
# @RELATION CALLS -> [ensure_encryption_key]
|
||||
# @RELATION CALLS -> [init_db]
|
||||
# @RELATION CALLS -> [seed_permissions]
|
||||
# @POST: auth.db is initialized with the correct schema and seeded permissions.
|
||||
# @RELATION CALLS -> ensure_encryption_key
|
||||
# @RELATION CALLS -> init_db
|
||||
# @RELATION CALLS -> seed_permissions
|
||||
def run_init():
|
||||
with belief_scope("init_auth_db"):
|
||||
logger.info("Initializing authentication database...")
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
# #region MigrateSqliteToPostgresScript [C:3] [TYPE Module] [SEMANTICS migration, sqlite, postgresql, config, task_logs, task_records]
|
||||
#
|
||||
# @BRIEF Migrates legacy config and task history from SQLite/file storage to PostgreSQL.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Script is idempotent for task_records and app_configurations.
|
||||
# @RELATION READS_FROM -> [backend/tasks.db]
|
||||
# @RELATION READS_FROM -> [backend/config.json]
|
||||
# @RELATION WRITES_TO -> [postgresql.task_records]
|
||||
# @RELATION WRITES_TO -> [postgresql.task_logs]
|
||||
# @RELATION WRITES_TO -> [postgresql.app_configurations]
|
||||
#
|
||||
# @LAYER: Scripts
|
||||
# @RELATION READS_FROM -> backend/tasks.db
|
||||
# @RELATION READS_FROM -> backend/config.json
|
||||
# @RELATION WRITES_TO -> postgresql.task_records
|
||||
# @RELATION WRITES_TO -> postgresql.task_logs
|
||||
# @RELATION WRITES_TO -> postgresql.app_configurations
|
||||
#
|
||||
# @INVARIANT: Script is idempotent for task_records and app_configurations.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
@@ -21,11 +20,7 @@ from typing import Any, Dict, Iterable, Optional
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from src.core.logger import belief_scope
|
||||
from src.core.cot_logger import MarkerLogger
|
||||
log = MarkerLogger("MigrateSqliteToPostgres")
|
||||
|
||||
# [/SECTION]
|
||||
from src.core.logger import belief_scope, logger
|
||||
|
||||
|
||||
# #region Constants [TYPE Section]
|
||||
@@ -41,8 +36,8 @@ DEFAULT_TARGET_URL = os.getenv(
|
||||
|
||||
# #region _json_load_if_needed [C:3] [TYPE Function]
|
||||
# @BRIEF Parses JSON-like values from SQLite TEXT/JSON columns to Python objects.
|
||||
# @PRE value is scalar JSON/text/list/dict or None.
|
||||
# @POST Returns normalized Python object or original scalar value.
|
||||
# @PRE: value is scalar JSON/text/list/dict or None.
|
||||
# @POST: Returns normalized Python object or original scalar value.
|
||||
def _json_load_if_needed(value: Any) -> Any:
|
||||
with belief_scope("_json_load_if_needed"):
|
||||
if value is None:
|
||||
@@ -169,7 +164,9 @@ def _ensure_target_schema(engine) -> None:
|
||||
def _migrate_config(engine, legacy_config_path: Optional[Path]) -> int:
|
||||
with belief_scope("_migrate_config"):
|
||||
if legacy_config_path is None:
|
||||
log.reason("No legacy config.json found, skipping migration")
|
||||
logger.info(
|
||||
"[_migrate_config][Action] No legacy config.json found, skipping"
|
||||
)
|
||||
return 0
|
||||
|
||||
payload = json.loads(legacy_config_path.read_text(encoding="utf-8"))
|
||||
@@ -330,7 +327,9 @@ def run_migration(
|
||||
sqlite_path: Path, target_url: str, legacy_config_path: Optional[Path]
|
||||
) -> Dict[str, int]:
|
||||
with belief_scope("run_migration"):
|
||||
log.reason("Starting migration from SQLite to PostgreSQL", payload={"sqlite_path": str(sqlite_path), "target_url": target_url})
|
||||
logger.info(
|
||||
"[run_migration][Entry] sqlite=%s target=%s", sqlite_path, target_url
|
||||
)
|
||||
if not sqlite_path.exists():
|
||||
raise FileNotFoundError(f"SQLite source not found: {sqlite_path}")
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
# #region SeedPermissionsScript [C:3] [TYPE Module] [SEMANTICS setup, database, auth, permissions, seeding]
|
||||
#
|
||||
# @BRIEF Populates the auth database with initial system permissions.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Safe to run multiple times (idempotent).
|
||||
# @RELATION DEPENDS_ON -> [AuthSessionLocal]
|
||||
# @RELATION DEPENDS_ON -> [Permission]
|
||||
# @RELATION DEPENDS_ON -> [Role]
|
||||
# @RELATION DEPENDS_ON -> [AuthRepository]
|
||||
#
|
||||
# @LAYER: Scripts
|
||||
# @RELATION DEPENDS_ON -> AuthSessionLocal
|
||||
# @RELATION DEPENDS_ON -> Permission
|
||||
# @RELATION DEPENDS_ON -> Role
|
||||
# @RELATION DEPENDS_ON -> AuthRepository
|
||||
#
|
||||
# @INVARIANT: Safe to run multiple times (idempotent).
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -20,11 +19,10 @@ from src.core.database import AuthSessionLocal
|
||||
from src.models.auth import Permission, Role
|
||||
from src.core.auth.repository import AuthRepository
|
||||
from src.core.logger import logger, belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
# #region INITIAL_PERMISSIONS [C:3] [TYPE Constant]
|
||||
# @BRIEF Canonical bootstrap permission tuples seeded into auth storage.
|
||||
# @RELATION DEPENDS_ON -> [SeedPermissionsScript]
|
||||
# @RELATION DEPENDS_ON -> SeedPermissionsScript
|
||||
INITIAL_PERMISSIONS = [
|
||||
# Admin Permissions
|
||||
{"resource": "admin:users", "action": "READ"},
|
||||
@@ -61,12 +59,12 @@ INITIAL_PERMISSIONS = [
|
||||
|
||||
# #region seed_permissions [C:3] [TYPE Function]
|
||||
# @BRIEF Inserts missing permissions into the database.
|
||||
# @POST All INITIAL_PERMISSIONS exist in the DB.
|
||||
# @RELATION DEPENDS_ON -> [AuthSessionLocal]
|
||||
# @RELATION DEPENDS_ON -> [Permission]
|
||||
# @RELATION DEPENDS_ON -> [Role]
|
||||
# @RELATION DEPENDS_ON -> [AuthRepository]
|
||||
# @RELATION DEPENDS_ON -> [INITIAL_PERMISSIONS]
|
||||
# @POST: All INITIAL_PERMISSIONS exist in the DB.
|
||||
# @RELATION DEPENDS_ON -> AuthSessionLocal
|
||||
# @RELATION DEPENDS_ON -> Permission
|
||||
# @RELATION DEPENDS_ON -> Role
|
||||
# @RELATION DEPENDS_ON -> AuthRepository
|
||||
# @RELATION DEPENDS_ON -> INITIAL_PERMISSIONS
|
||||
def seed_permissions():
|
||||
with belief_scope("seed_permissions"):
|
||||
db = AuthSessionLocal()
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# #region SeedSupersetLoadTestScript [C:3] [TYPE Module] [SEMANTICS superset, load-test, charts, dashboards, seed, stress]
|
||||
#
|
||||
# @BRIEF Creates randomized load-test data in Superset by cloning chart configurations and creating dashboards in target environments.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Created chart and dashboard names are globally unique for one script run.
|
||||
# @LAYER: Scripts
|
||||
# @RELATION USES -> [ConfigManager]
|
||||
# @RELATION USES -> [SupersetClient]
|
||||
#
|
||||
# @INVARIANT: Created chart and dashboard names are globally unique for one script run.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import argparse
|
||||
import json
|
||||
import random
|
||||
@@ -19,18 +18,14 @@ sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from src.core.config_manager import ConfigManager
|
||||
from src.core.config_models import Environment
|
||||
from src.core.logger import belief_scope
|
||||
from src.core.cot_logger import MarkerLogger
|
||||
from src.core.logger import belief_scope, logger
|
||||
from src.core.superset_client import SupersetClient
|
||||
log = MarkerLogger("SeedSupersetLoadTest")
|
||||
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# #region _parse_args [TYPE Function]
|
||||
# @BRIEF Parses CLI arguments for load-test data generation.
|
||||
# @PRE Script is called from CLI.
|
||||
# @POST Returns validated argument namespace.
|
||||
# @PRE: Script is called from CLI.
|
||||
# @POST: Returns validated argument namespace.
|
||||
def _parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Seed Superset with load-test charts and dashboards"
|
||||
@@ -73,8 +68,8 @@ def _parse_args() -> argparse.Namespace:
|
||||
|
||||
# #region _extract_result_payload [TYPE Function]
|
||||
# @BRIEF Normalizes Superset API payloads that may be wrapped in `result`.
|
||||
# @PRE payload is a JSON-decoded API response.
|
||||
# @POST Returns the unwrapped object when present.
|
||||
# @PRE: payload is a JSON-decoded API response.
|
||||
# @POST: Returns the unwrapped object when present.
|
||||
def _extract_result_payload(payload: Dict) -> Dict:
|
||||
result = payload.get("result")
|
||||
if isinstance(result, dict):
|
||||
@@ -87,8 +82,8 @@ def _extract_result_payload(payload: Dict) -> Dict:
|
||||
|
||||
# #region _extract_created_id [TYPE Function]
|
||||
# @BRIEF Extracts object ID from create/update API response.
|
||||
# @PRE payload is a JSON-decoded API response.
|
||||
# @POST Returns integer object ID or None if missing.
|
||||
# @PRE: payload is a JSON-decoded API response.
|
||||
# @POST: Returns integer object ID or None if missing.
|
||||
def _extract_created_id(payload: Dict) -> Optional[int]:
|
||||
direct_id = payload.get("id")
|
||||
if isinstance(direct_id, int):
|
||||
@@ -104,8 +99,8 @@ def _extract_created_id(payload: Dict) -> Optional[int]:
|
||||
|
||||
# #region _generate_unique_name [TYPE Function]
|
||||
# @BRIEF Generates globally unique random names for charts/dashboards.
|
||||
# @PRE used_names is mutable set for collision tracking.
|
||||
# @POST Returns a unique string and stores it in used_names.
|
||||
# @PRE: used_names is mutable set for collision tracking.
|
||||
# @POST: Returns a unique string and stores it in used_names.
|
||||
def _generate_unique_name(prefix: str, used_names: set[str], rng: random.Random) -> str:
|
||||
adjectives = [
|
||||
"amber",
|
||||
@@ -144,8 +139,8 @@ def _generate_unique_name(prefix: str, used_names: set[str], rng: random.Random)
|
||||
|
||||
# #region _resolve_target_envs [TYPE Function]
|
||||
# @BRIEF Resolves requested environment IDs from configuration.
|
||||
# @PRE env_ids is non-empty.
|
||||
# @POST Returns mapping env_id -> configured environment object.
|
||||
# @PRE: env_ids is non-empty.
|
||||
# @POST: Returns mapping env_id -> configured environment object.
|
||||
def _resolve_target_envs(env_ids: List[str]) -> Dict[str, Environment]:
|
||||
config_manager = ConfigManager()
|
||||
configured = {env.id: env for env in config_manager.get_environments()}
|
||||
@@ -162,7 +157,9 @@ def _resolve_target_envs(env_ids: List[str]) -> Dict[str, Environment]:
|
||||
env = Environment(**row)
|
||||
configured[env.id] = env
|
||||
except Exception as exc:
|
||||
log.explore("Failed loading environments from config", payload={"config_path": str(config_path)}, error=str(exc))
|
||||
logger.warning(
|
||||
f"[REFLECT] Failed loading environments from {config_path}: {exc}"
|
||||
)
|
||||
|
||||
for env_id in env_ids:
|
||||
env = configured.get(env_id)
|
||||
@@ -178,8 +175,8 @@ def _resolve_target_envs(env_ids: List[str]) -> Dict[str, Environment]:
|
||||
|
||||
# #region _build_chart_template_pool [TYPE Function]
|
||||
# @BRIEF Builds a pool of source chart templates to clone in one environment.
|
||||
# @PRE Client is authenticated.
|
||||
# @POST Returns non-empty list of chart payload templates.
|
||||
# @PRE: Client is authenticated.
|
||||
# @POST: Returns non-empty list of chart payload templates.
|
||||
def _build_chart_template_pool(
|
||||
client: SupersetClient, pool_size: int, rng: random.Random
|
||||
) -> List[Dict]:
|
||||
@@ -253,9 +250,9 @@ def _build_chart_template_pool(
|
||||
|
||||
# #region seed_superset_load_data [TYPE Function]
|
||||
# @BRIEF Creates dashboards and cloned charts for load testing across target environments.
|
||||
# @PRE Target environments must be reachable and authenticated.
|
||||
# @POST Returns execution statistics dictionary.
|
||||
# @SIDE_EFFECT Creates objects in Superset environments.
|
||||
# @PRE: Target environments must be reachable and authenticated.
|
||||
# @POST: Returns execution statistics dictionary.
|
||||
# @SIDE_EFFECT: Creates objects in Superset environments.
|
||||
def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
rng = random.Random(args.seed)
|
||||
env_map = _resolve_target_envs(args.envs)
|
||||
@@ -274,7 +271,9 @@ def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
templates_by_env[env_id] = _build_chart_template_pool(
|
||||
client, args.template_pool_size, rng
|
||||
)
|
||||
log.reason("Loaded chart templates for environment", payload={"env_id": env_id, "template_count": len(templates_by_env[env_id])})
|
||||
logger.info(
|
||||
f"[REASON] Environment {env_id}: loaded {len(templates_by_env[env_id])} chart templates"
|
||||
)
|
||||
|
||||
errors = 0
|
||||
env_ids = list(env_map.keys())
|
||||
@@ -286,7 +285,9 @@ def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
dashboard_title = _generate_unique_name("lt_dash", used_dashboard_names, rng)
|
||||
|
||||
if args.dry_run:
|
||||
log.reflect("Dry-run dashboard create", payload={"env_id": env_id, "title": dashboard_title})
|
||||
logger.info(
|
||||
f"[REFLECT] Dry-run dashboard create: env={env_id}, title={dashboard_title}"
|
||||
)
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -300,7 +301,7 @@ def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
created_dashboards[env_id].append(dashboard_id)
|
||||
except Exception as exc:
|
||||
errors += 1
|
||||
log.explore("Failed creating dashboard", payload={"env_id": env_id}, error=str(exc))
|
||||
logger.error(f"[EXPLORE] Failed creating dashboard in {env_id}: {exc}")
|
||||
if errors >= args.max_errors:
|
||||
raise RuntimeError(
|
||||
f"Stopping due to max errors reached ({errors})"
|
||||
@@ -350,10 +351,10 @@ def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
created_charts[env_id].append(chart_id)
|
||||
|
||||
if (index + 1) % 500 == 0:
|
||||
log.reason(f"Created {index + 1}/{args.charts} charts")
|
||||
logger.info(f"[REASON] Created {index + 1}/{args.charts} charts")
|
||||
except Exception as exc:
|
||||
errors += 1
|
||||
log.explore("Failed creating chart", payload={"env_id": env_id}, error=str(exc))
|
||||
logger.error(f"[EXPLORE] Failed creating chart in {env_id}: {exc}")
|
||||
if errors >= args.max_errors:
|
||||
raise RuntimeError(
|
||||
f"Stopping due to max errors reached ({errors})"
|
||||
@@ -374,13 +375,15 @@ def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
|
||||
# #region main [TYPE Function]
|
||||
# @BRIEF CLI entrypoint for Superset load-test data seeding.
|
||||
# @PRE Command line arguments are valid.
|
||||
# @POST Prints summary and exits with non-zero status on failure.
|
||||
# @PRE: Command line arguments are valid.
|
||||
# @POST: Prints summary and exits with non-zero status on failure.
|
||||
def main() -> None:
|
||||
with belief_scope("seed_superset_load_test.main"):
|
||||
args = _parse_args()
|
||||
result = seed_superset_load_data(args)
|
||||
log.reflect("Seed load test complete", payload={"summary": result})
|
||||
logger.info(
|
||||
f"[COHERENCE:OK] Result summary: {json.dumps(result, ensure_ascii=True)}"
|
||||
)
|
||||
|
||||
|
||||
# #endregion main
|
||||
|
||||
Reference in New Issue
Block a user