Files
autotest/polygon-docs/decouple-plan.md
T

4.7 KiB
Raw Blame History

План: Decouple polygon v0.2.1

Цель: разнести монолитный app.py (400 строк, 17 роутов) на отдельные blueprint-файлы по шаблону app-autotest. Никакого инлайн-CSS/HTML — всё в static/ и templates/.

Текущее → Целевое

polygon/site/
├── app.py (400 строк, всё в одном)
├── mock_state.py
├── state_machine.py
├── config_loader.py
└── from_stands.py

          ↓

polygon/site/
├── app.py                    # ТОЛЬКО: Flask(), register_blueprint, /health, app.run()
├── routes/
│   ├── root.py               # GET / (render_template + style.css)
│   ├── services_routes.py    # GET /api/v1/svc/services, GET /services/<id>
│   ├── instances_routes.py   # GET/POST /api/v1/svc/instances, GET /instances/<uid>
│   ├── operations_routes.py  # POST /instanceOperations, GET default, GET status, POST params, GET validate
│   ├── run.py                # POST /instanceOperations/<uid>/run
│   └── mock_routes.py        # POST /_mock/reset, GET /_mock/state, GET /_mock/services, POST /_mock/delay
├── state/
│   ├── mock_state.py         # MockState (без изменений)
│   └── state_machine.py      # apply_effect (без изменений)
├── config/
│   └── loader.py             # load_services() (бывший config_loader.py)
├── converter/
│   └── from_stands.py        # конвертер (без изменений)
├── utils/
│   ├── pluralize.py          # _pluralize() — одна функция
│   └── now.py                # _now() — одна функция
├── static/
│   └── style.css             # CSS из index() — тёмная тема
├── templates/
│   └── index.html            # HTML из index() — Jinja2 с {{ VERSION }}
└── services/
    └── ...yaml               # без изменений

Пошагово

Шаг 1. Утилиты

  • utils/now.py_now() из app.py (одна функция)
  • utils/pluralize.py_pluralize() из state_machine.py (одна функция)

Шаг 2. CSS и HTML

  • static/style.css — вынести инлайн-CSS из f-строки index()
  • templates/index.html — вынести HTML из f-строки, использовать Jinja2 {{ version }}, <link rel="stylesheet">

Шаг 3. Маршруты — 6 blueprint-файлов

Каждый blueprint импортирует нужные модули из state/, config/, utils/.

  • routes/root.pybp = Blueprint("root", __name__), роут / с render_template
  • routes/services_routes.pybp = Blueprint("services", __name__), 2 роута
  • routes/instances_routes.pybp = Blueprint("instances", __name__), 3 роута
  • routes/operations_routes.pybp = Blueprint("operations", __name__), 5 роутов
  • routes/run.pybp = Blueprint("run", __name__), 1 роут
  • routes/mock_routes.pybp = Blueprint("mock", __name__), 4 роута

Шаг 4. app.py — только скелет

import os
from flask import Flask
from routes.root import bp as root_bp
from routes.services_routes import bp as services_bp
# ... все 6 blueprint'ов
from config.loader import load_services

os.environ.setdefault("WEB_CONCURRENCY", "1")
VERSION = "0.2.1"
MOCK_OP_DELAY = float(os.getenv("MOCK_OP_DELAY", "0.1"))

app = Flask(__name__, template_folder="templates", static_folder="static")

app.register_blueprint(root_bp)
app.register_blueprint(services_bp)
# ... все 6

@app.route("/health")
def health():
    return "OK"

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=5000)

Шаг 5. Импорты в state_machine и from_stands

  • state_machine.py: from utils.pluralize import pluralize (вместо _pluralize() внутри)
  • from_stands.py: импортирует pluralize из utils/

Что НЕ меняется

  • state/mock_state.py — как есть
  • state/state_machine.py — только импорт pluralize
  • converter/from_stands.py — только импорт pluralize
  • services/*.yaml — данные
  • tests/ — только поправить пути импорта (site → site.state и т.д.)

Верификация

  1. python -m py_compile на ВСЕХ .py файлах
  2. python -c "from app import app; [print(r.rule) for r in app.url_map.iter_rules()]" → те же 17 роутов
  3. pytest tests/ -v → 19/19 PASS
  4. Запустить python app.py → curl /health, /services, /instances → работает