# [MODULE] Сущности данных конфигурации # @desc: Определяет структуры данных, используемые для конфигурации и трансформации в инструменте Superset. # @contracts: # - Все модели наследуются от `pydantic.BaseModel` для автоматической валидации. # - Валидация URL-адресов и параметров аутентификации. # - Валидация структуры конфигурации БД для миграций. # @coherence: # - Все модели согласованы со схемой API Superset v1. # - Совместимы с клиентскими методами `SupersetClient` и утилитами. # [IMPORTS] Pydantic и Typing from typing import Optional, Dict, Any, Union from pydantic import BaseModel, validator, Field, HttpUrl # [COHERENCE_CHECK_PASSED] Все необходимые импорты для Pydantic моделей. # [IMPORTS] Локальные модули from .utils.logger import SupersetLogger class SupersetConfig(BaseModel): """[CONFIG] Конфигурация подключения к Superset API. @semantic: Инкапсулирует основные параметры, необходимые для инициализации `SupersetClient`. @invariant: - `base_url` должен быть валидным HTTP(S) URL и содержать `/api/v1`. - `auth` должен содержать обязательные поля для аутентификации по логину/паролю. - `timeout` должен быть положительным числом. """ base_url: str = Field(..., description="Базовый URL Superset API, включая версию /api/v1.", pattern=r'.*/api/v1.*') auth: Dict[str, str] = Field(..., description="Словарь с данными для аутентификации (provider, username, password, refresh).") verify_ssl: bool = Field(True, description="Флаг для проверки SSL-сертификатов.") timeout: int = Field(30, description="Таймаут в секундах для HTTP-запросов.") logger: Optional[SupersetLogger] = Field(None, description="Экземпляр логгера для логирования внутри клиента.") # [VALIDATOR] Проверка параметров аутентификации @validator('auth') def validate_auth(cls, v: Dict[str, str]) -> Dict[str, str]: """[CONTRACT_VALIDATOR] Валидация словаря `auth`. @pre: - `v` должен быть словарем. @post: - Возвращает `v` если все обязательные поля присутствуют. @raise: - `ValueError`: Если отсутствуют обязательные поля ('provider', 'username', 'password', 'refresh'). """ required = {'provider', 'username', 'password', 'refresh'} if not required.issubset(v.keys()): raise ValueError( f"[CONTRACT_VIOLATION] Словарь 'auth' должен содержать поля: {required}. " f"Отсутствующие: {required - v.keys()}" ) # [COHERENCE_CHECK_PASSED] Auth-конфигурация валидна. return v # [VALIDATOR] Проверка base_url @validator('base_url') def check_base_url_format(cls, v: str) -> str: """[CONTRACT_VALIDATOR] Валидация формата `base_url`. @pre: - `v` должна быть строкой. @post: - Возвращает `v` если это валидный URL. @raise: - `ValueError`: Если URL невалиден. """ try: # Для Pydantic v2: from pydantic import HttpUrl HttpUrl(v, scheme="https") # Явное указание схемы except ValueError: # Для совместимости с Pydantic v1: HttpUrl(v) return v class Config: arbitrary_types_allowed = True # Разрешаем Pydantic обрабатывать произвольные типы (например, SupersetLogger) json_schema_extra = { "example": { "base_url": "https://host/api/v1/", "auth": { "provider": "db", "username": "user", "password": "pass", "refresh": True }, "verify_ssl": True, "timeout": 60 } } # [SEMANTIC-TYPE] Конфигурация БД для миграций class DatabaseConfig(BaseModel): """[CONFIG] Параметры трансформации баз данных при миграции дашбордов. @semantic: Содержит `old` и `new` состояния конфигурации базы данных, используемые для поиска и замены в YAML-файлах экспортированных дашбордов. @invariant: - `database_config` должен быть словарем с ключами 'old' и 'new'. - Каждое из 'old' и 'new' должно быть словарем, содержащим метаданные БД Superset. """ database_config: Dict[str, Dict[str, Any]] = Field(..., description="Словарь, содержащий 'old' и 'new' конфигурации базы данных.") logger: Optional[SupersetLogger] = Field(None, description="Экземпляр логгера для логирования.") @validator('database_config') def validate_config(cls, v: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, Any]]: """[CONTRACT_VALIDATOR] Валидация словаря `database_config`. @pre: - `v` должен быть словарем. @post: - Возвращает `v` если содержит ключи 'old' и 'new'. @raise: - `ValueError`: Если отсутствуют ключи 'old' или 'new'. """ if not {'old', 'new'}.issubset(v.keys()): raise ValueError( "[CONTRACT_VIOLATION] 'database_config' должен содержать ключи 'old' и 'new'." ) # Дополнительно можно добавить проверку структуры `old` и `new` на наличие `uuid`, `database_name` и т.д. # Для простоты пока ограничимся наличием ключей 'old' и 'new'. # [COHERENCE_CHECK_PASSED] Конфигурация базы данных для миграции валидна. return v class Config: arbitrary_types_allowed = True json_schema_extra = { "example": { "database_config": { "old": { "database_name": "Prod Clickhouse", "sqlalchemy_uri": "clickhousedb+connect://clicketl:XXXXXXXXXX@rgm-s-khclk.hq.root.ad:443/dm", "uuid": "b9b67cb5-9874-4dc6-87bd-354fc33be6f9", "database_uuid": "b9b67cb5-9874-4dc6-87bd-354fc33be6f9", "allow_ctas": "false", "allow_cvas": "false", "allow_dml": "false" }, "new": { "database_name": "Dev Clickhouse", "sqlalchemy_uri": "clickhousedb+connect://dwhuser:XXXXXXXXXX@10.66.229.179:8123/dm", "uuid": "e9fd8feb-cb77-4e82-bc1d-44768b8d2fc2", "database_uuid": "e9fd8feb-cb77-4e82-bc1d-44768b8d2fc2", "allow_ctas": "true", "allow_cvas": "true", "allow_dml": "true" } } } }