# # @SEMANTICS: pydantic, model, config, validation, data-structure # @PURPOSE: Определяет Pydantic-модели для конфигурации инструмента, обеспечивая валидацию данных. # @DEPENDS_ON: pydantic -> Для создания моделей и валидации. # @DEPENDS_ON: superset_tool.utils.logger -> Для логирования в процессе валидации. # import re from typing import Optional, Dict, Any from pydantic import BaseModel, validator, Field from .utils.logger import SupersetLogger # # --- Начало кода модуля --- # # @PURPOSE: Модель конфигурации для подключения к одному экземпляру Superset API. # @INHERITS_FROM: pydantic.BaseModel class SupersetConfig(BaseModel): env: str = Field(..., description="Название окружения (например, dev, prod).") base_url: str = Field(..., description="Базовый URL Superset API, включая /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="Экземпляр логгера для логирования.") # # @PURPOSE: Проверяет, что словарь `auth` содержит все необходимые для аутентификации поля. # @PRE: `v` должен быть словарем. # @POST: Возвращает `v`, если все обязательные поля (`provider`, `username`, `password`, `refresh`) присутствуют. # @THROW: ValueError - Если отсутствуют обязательные поля. @validator('auth') def validate_auth(cls, v: Dict[str, str]) -> Dict[str, str]: required = {'provider', 'username', 'password', 'refresh'} if not required.issubset(v.keys()): raise ValueError(f"Словарь 'auth' должен содержать поля: {required}. Отсутствующие: {required - v.keys()}") return v # # # @PURPOSE: Проверяет, что `base_url` соответствует формату URL и содержит `/api/v1`. # @PRE: `v` должна быть строкой. # @POST: Возвращает очищенный `v`, если формат корректен. # @THROW: ValueError - Если формат URL невалиден. @validator('base_url') def check_base_url_format(cls, v: str) -> str: v = v.strip() if not re.fullmatch(r'https?://.+/api/v1/?(?:.*)?', v): raise ValueError(f"Invalid URL format: {v}. Must include '/api/v1'.") return v # class Config: arbitrary_types_allowed = True # # # @PURPOSE: Модель для параметров трансформации баз данных при миграции дашбордов. # @INHERITS_FROM: pydantic.BaseModel class DatabaseConfig(BaseModel): database_config: Dict[str, Dict[str, Any]] = Field(..., description="Словарь, содержащий 'old' и 'new' конфигурации базы данных.") logger: Optional[SupersetLogger] = Field(None, description="Экземпляр логгера для логирования.") # # @PURPOSE: Проверяет, что словарь `database_config` содержит ключи 'old' и 'new'. # @PRE: `v` должен быть словарем. # @POST: Возвращает `v`, если ключи 'old' и 'new' присутствуют. # @THROW: ValueError - Если отсутствуют обязательные ключи. @validator('database_config') def validate_config(cls, v: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, Any]]: if not {'old', 'new'}.issubset(v.keys()): raise ValueError("'database_config' должен содержать ключи 'old' и 'new'.") return v # class Config: arbitrary_types_allowed = True # # --- Конец кода модуля --- #