- loader.py: STANDS dict, get_services/get_ops_index per stand, POLYGON_STANDS env
- mock_state.py: get_state(stand_id) — ленивая инициализация
- app.py: StandMiddleware — перезапись PATH_INFO до Flask-роутинга
- Все роуты: LocalProxy(state), LocalProxy(SERVICES/OPS_INDEX) через g.stand_id
- openapi.py: _build_servers() — по одному серверу на стенд
- services/stand{1,2,3}/ — 37 YAML на стенд из STANDS/dev и STANDS/test
- tests/test_api.py: +4 multi-stand теста (изоляция, fail-next, OpenAPI servers)
- Обратная совместимость: /api/v1/svc/... → стенд по умолчанию
99 lines
3.0 KiB
Python
99 lines
3.0 KiB
Python
"""
|
|
config/loader.py — загрузка сервисов и глобальные конфигурационные переменные.
|
|
|
|
Поддержка нескольких стендов:
|
|
Переменная POLYGON_STANDS задаёт список стендов в формате:
|
|
имя:путь,имя:путь,...
|
|
Например:
|
|
POLYGON_STANDS=stand1:services/stand1,stand2:services/stand2,stand3:services/stand3
|
|
|
|
По умолчанию — один стенд "default" из папки services/.
|
|
|
|
Переменные модуля:
|
|
VERSION — str, версия полигона
|
|
STANDS — {stand_id: {"SERVICES": ..., "OPS_INDEX": ...}}
|
|
DELAY — float, задержка операции
|
|
DEFAULT_STAND — str, ID стенда по умолчанию
|
|
"""
|
|
|
|
import os
|
|
import yaml
|
|
|
|
_YAML_EXTS = (".yaml", ".yml")
|
|
_DEFAULT_SERVICES_DIR = "services"
|
|
|
|
|
|
def _load_one(services_dir):
|
|
"""Загружает YAML из одной директории → (services, ops_index)."""
|
|
if not os.path.isdir(services_dir):
|
|
raise FileNotFoundError(f"Services directory not found: {services_dir}")
|
|
|
|
services = {}
|
|
ops_index = {}
|
|
|
|
for fname in sorted(os.listdir(services_dir)):
|
|
if not fname.lower().endswith(_YAML_EXTS):
|
|
continue
|
|
path = os.path.join(services_dir, fname)
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
cfg = yaml.safe_load(f)
|
|
if not cfg:
|
|
continue
|
|
svc_id = cfg["service_id"]
|
|
services[svc_id] = cfg
|
|
for op in cfg.get("operations", []):
|
|
ops_index[op["svcOperationId"]] = cfg
|
|
|
|
return services, ops_index
|
|
|
|
|
|
def _parse_stands(raw):
|
|
"""Парсит POLYGON_STANDS → {stand_id: services_dir}."""
|
|
stands = {}
|
|
for pair in raw.split(","):
|
|
pair = pair.strip()
|
|
if ":" not in pair:
|
|
continue
|
|
name, path = pair.split(":", 1)
|
|
stands[name.strip()] = path.strip()
|
|
return stands
|
|
|
|
|
|
# ── Загрузка ──
|
|
|
|
_raw = os.getenv("POLYGON_STANDS", "")
|
|
if _raw:
|
|
_stand_dirs = _parse_stands(_raw)
|
|
else:
|
|
_stand_dirs = {"default": _DEFAULT_SERVICES_DIR}
|
|
|
|
STANDS = {}
|
|
for _sid, _sdir in _stand_dirs.items():
|
|
_svc, _ops = _load_one(_sdir)
|
|
STANDS[_sid] = {"SERVICES": _svc, "OPS_INDEX": _ops}
|
|
|
|
DEFAULT_STAND = next(iter(STANDS.keys()))
|
|
|
|
# Для обратной совместимости (код который не использует g.stand_id)
|
|
_DF = STANDS[DEFAULT_STAND]
|
|
SERVICES = _DF["SERVICES"]
|
|
OPS_INDEX = _DF["OPS_INDEX"]
|
|
|
|
DELAY = float(os.getenv("MOCK_OP_DELAY", "0.1"))
|
|
VERSION = "0.5.0"
|
|
|
|
|
|
def get_services(stand_id):
|
|
"""SERVICES для конкретного стенда."""
|
|
return STANDS[stand_id]["SERVICES"]
|
|
|
|
|
|
def get_ops_index(stand_id):
|
|
"""OPS_INDEX для конкретного стенда."""
|
|
return STANDS[stand_id]["OPS_INDEX"]
|
|
|
|
|
|
def list_stands():
|
|
"""Список ID всех стендов."""
|
|
return list(STANDS.keys())
|