Files
autotest/polygon-docs/sonnet-code-review-prompt.md
T

186 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Задача: Code Review polygon v0.2.0
> Адресат: Claude Sonnet 4.6 (новый чат, с нуля)
> Дата: 2026-07-31
> ⛔ ОТВЕТ — ТОЛЬКО В ЧАТ. Не редактировать файлы. Не создавать файлы.
---
## 1. Что такое polygon
**Polygon** — эмулятор REST API облачной платформы Nubes. Отдельный managed-сервис
(`polygon.pythonk8s.dev.nubes.ru`), притворяется реальным Nubes API для интеграционных
тестов приложения **app-autotest**.
- Flask 3.0 + gunicorn, деплой на Nubes pythonk8s
- Все данные в памяти (MockState), без БД
- Data-driven: конфиги сервисов генерируются из STANDS YAML через `from_stands.py`
- 37 сервисов (dummy, postgres, redis, kafka, flask, ...)
- 17 API-эндпоинтов, префикс `/api/v1/svc`
- 19 юнит-тестов (все PASS)
Репозиторий: `https://gitea.services.ngcloud.ru/forcloud/polygon.git` (ветка `master`)
Деплой: `https://polygon.pythonk8s.dev.nubes.ru/`
Версия: **v0.2.0**
## 2. Структура кода
```
polygon/
├── requirements.txt # Flask>=3.0, gunicorn>=21.2, PyYAML>=6.0
├── .gitignore
├── tests/
│ ├── test_converter.py # 10 юнит-тестов from_stands.py
│ └── test_state_machine.py # 9 юнит-тестов state_machine.py + mock_state
└── site/
├── app.py # Flask-приложение (17 эндпоинтов)
├── mock_state.py # MockState: instances, operations, op_params в памяти
├── state_machine.py # apply_effect() — мутация состояния по kind/action
├── config_loader.py # загрузка services/*.yaml → {svcId: def} + {opId: def}
├── from_stands.py # конвертер STANDS YAML → polygon config
└── services/ # 37 сгенерированных YAML-конфигов
```
## 3. Что делает каждый модуль
### app.py — 17 эндпоинтов
Полный эмулятор Nubes API. Порядок маршрутов критичен: `default/<int>` ДО `<uid>`.
| # | Метод | Путь | Назначение |
|---|-------|------|------------|
| 1 | GET | `/health` | `"OK"` (для Nubes healthcheck) |
| 2 | GET | `/` | HTML с версией, счётчиками |
| 3 | GET | `/api/v1/svc/services` | список сервисов |
| 4 | GET | `/api/v1/svc/services/<id>` | операции сервиса |
| 5 | GET | `/api/v1/svc/instances` | пагинация |
| 6 | GET | `/api/v1/svc/instances/<uid>` | инстанс + state.params + state.out |
| 7 | POST | `/api/v1/svc/instances` | создать shell → 201 + `Location: ./{uid}` |
| 8 | GET | `/api/v1/svc/instanceOperations/default/<id>` | cfsParams с dataDescriptor, valueList |
| 9 | POST | `/api/v1/svc/instanceOperations` | создать операцию → 201 + Location |
| 10 | GET | `/api/v1/svc/instanceOperations/<uid>` | статус + cfsParams (?fields=...) |
| 11 | POST | `/api/v1/svc/instanceOperationCfsParams` | paramId → value |
| 12 | GET | `/api/v1/svc/instanceOperations/<uid>/validate-cfs` | **пустое тело**, 200 |
| 13 | POST | `/api/v1/svc/instanceOperations/<uid>/run` | sleep → apply_effect → dtFinish |
| 14 | POST | `/api/v1/svc/_mock/reset` | сброс |
| 15 | GET | `/api/v1/svc/_mock/state` | отладка |
| 16 | GET | `/api/v1/svc/_mock/services` | отладка |
| 17 | POST | `/api/v1/svc/_mock/delay/<s>` | MOCK_OP_DELAY |
**Критические точки:**
- `POST /instances` и `POST /instanceOperations` **обязаны** отдавать `Location: ./{uuid}` — app-autotest достаёт UUID из заголовка
- `POST /instanceOperations` при `operation=="create"` — тело НЕ содержит `svcOperationId`, polygon ищет сам
- `validate-cfs`: `return "", 200` (НЕ `jsonify`) — app-autotest ждёт пустое тело
- `run`: синхронный `time.sleep(MOCK_OP_DELAY)` + `apply_effect` + `dtFinish = now`
- `MOCK_OP_DELAY` из env, по умолчанию 0.1с
### mock_state.py — состояние в памяти
```python
class MockState:
instances = {} # instanceUid → {instanceUid, serviceId, displayName, status, state: {params, out}, ...}
operations = {} # opUid → {instanceOperationUid, instanceUid, svcOperationId, operation, kind, action, dtStart, dtFinish, isSuccessful, ...}
op_params = {} # opUid → {paramId(int): paramValue(str)}
```
Методы: `create_instance`, `get_instance`, `list_instances` (пагинация), `create_operation`,
`get_operation`, `set_param`, `get_params`, `reset`.
UUID через `uuid.uuid4()`. Пагинация: pageSize ≤ 200, стоп по `len(batch) < pageSize`.
### state_machine.py — apply_effect
Мутирует MockState после завершения операции:
| kind | action | Эффект |
|------|--------|--------|
| instance | create | статус `running`, stateParams из шаблона, stateOut из шаблона |
| instance | modify | мерж op_params в state.params через cfsParamsByOp |
| instance | delete | удалить инстанс |
| instance | suspend | статус `suspended` |
| instance | resume | статус `running` |
| instance | redeploy | статус `running` |
| instance | restart/recovery/... | no-op |
| subresource | create | `state.out[plural][name] = {}` |
| subresource | delete | `del state.out[plural][name]` |
`_extract_subresource_name`: ищет первое непустое строковое значение в op_params, fallback на subresource_name.
### config_loader.py — загрузка конфигов
Читает все `.yaml` из `services/`. Возвращает:
- `services`: `{service_id: service_def}`
- `ops_index`: `{svcOperationId: service_def}` — для поиска сервиса по ID операции
### from_stands.py — конвертер
Конвертирует STANDS YAML (из Terraform-провайдера) → polygon-конфиг.
Запуск: `python from_stands.py <STANDS_DIR> [SERVICES_DIR]`
- `html.unescape()` для всех строк (`&gt;``>`, `&quot;``"`)
- Маппинг полей: `id``svcOperationCfsParamId`, `code``svcOperationCfsParam`, `data_type``dataType`, `value_list``valueList`, `sub_params``dataDescriptor`
- `dataDescriptor` генерируется для всех типов с `sub_params` (map, map-fixed, array-map-fixed)
- `state_out_template`: авто-генерация из операций с `kind=subresource, action=create`
- `stateParams`: defaults из create-операции, JSON-генерация для map-fixed
- `cfsParamsByOp`: связка opId → список paramId
## 4. Что проверять (фокус code review)
### Безопасность
- Все ли входные данные валидируются (JSON body, query params)?
- Есть ли возможность инъекции через `displayName`, `paramValue`?
- `_mock/*` эндпоинты — не утечка ли это на проде?
### Корректность API
- Совпадают ли форматы ответов с реальным Nubes API?
- Правильно ли обрабатываются краевые случаи: отсутствующий сервис, невалидный instanceUid, пустой body?
- Корректны ли статус-коды (201, 400, 404)?
- `Location`-заголовки правильного формата?
### Состояние и стейт-машина
- Нет ли гонок в MockState (хотя воркер один, но Flask debug mode reloads)?
- Все ли переходы стейт-машины корректны?
- Не теряются ли данные при modify/reset?
- Правильно ли работает пагинация при пустом/частичном списке?
### Конвертер
- Все ли краевые случаи STANDS YAML обрабатываются (пустые поля, отсутствующие sub_params)?
- Правильно ли `html.unescape` применяется ко всем строковым полям?
- Не падает ли на нестандартных YAML (template, s3bucket)?
### Код и архитектура
- Нет ли дублирования логики?
- Понятны ли имена функций/переменных?
- Нет ли мёртвого кода?
- Правильно ли обрабатываются ошибки (try/except где нужно)?
### Nubes-совместимость
- `site/__init__.py` отсутствует?
- `app.run(host="0.0.0.0", port=5000)` на месте?
- `from site.xxx` нигде нет?
- `/health` возвращает `"OK"`?
## 5. Формат ответа
Сгруппируй находки по категориям:
- 🔴 Критические (сломает работу)
- 🟡 Средние (потенциальная проблема)
- 🔵 Минорные (стиль, имена)
Для каждой находки: файл, строка (примерная), проблема, предлагаемое исправление.
Если код в порядке — скажи что всё ок по каждой категории.
## 6. Что можно спрашивать у меня
Можешь задавать уточняющие вопросы. Например:
- «Какой формат у real Nubes API для эндпоинта X?»
- «Почему сделано так, а не иначе?»
- «Какие именно поля ждёт app-autotest в ответе Y?»
Формат вопросов:
```
### Вопрос N: <краткий заголовок>
<развёрнутый вопрос>
```