From a95606ac0a0487c230493d61c958a43f61738008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 9 Aug 2026 14:50:02 +0400 Subject: [PATCH] =?UTF-8?q?Docs:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=20api-operation-stages=20=D0=B2=20DOCS/=20=D0=B8=D0=B7?= =?UTF-8?q?=20LEGACY=20+=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20=D1=81?= =?UTF-8?q?=D1=81=D1=8B=D0=BB=D0=BE=D0=BA=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DOCS/LEGACY/api-operation-stages.md | 85 --------------------- DOCS/api-operation-stages.md | 112 ++++++++++++++++++++++++++++ README.md | 4 +- 3 files changed, 114 insertions(+), 87 deletions(-) delete mode 100644 DOCS/LEGACY/api-operation-stages.md create mode 100644 DOCS/api-operation-stages.md diff --git a/DOCS/LEGACY/api-operation-stages.md b/DOCS/LEGACY/api-operation-stages.md deleted file mode 100644 index 6786da6..0000000 --- a/DOCS/LEGACY/api-operation-stages.md +++ /dev/null @@ -1,85 +0,0 @@ -# Стадии выполнения операции (stages) - -## Эндпоинт - -``` -GET /instanceOperations/{operationUid} -``` - -## Поле `stages` - -В ответе `instanceOperation.stages` — массив объектов-стадий. Каждая стадия: - -| Поле | Тип | Описание | -|------|-----|---------| -| `instanceOperationStageUid` | UUID | UID стадии | -| `stage` | string | Название (напр. `"2. Основной процесс"`) | -| `isSuccessful` | bool/null | `true`/`false`/`null` (ещё не завершена) | -| `dtStart` | datetime | Начало | -| `dtFinish` | datetime/null | Окончание (`null` — ещё идёт) | -| `duration` | float | Секунды (пересчитывается пока идёт: `729.2`) | -| `stageMsg` | JSON-string/null | Детальные логи этапа | - -## Пример - -Запрос: -```bash -curl -s -H "Authorization: Bearer $TOKEN" -H "User-Agent: Mozilla/5.0" \ - "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc/instanceOperations/{opUid}" -``` - -Ответ (фрагмент): -```json -{ - "instanceOperation": { - "instanceOperationUid": "a955a902-...", - "operation": "create", - "isInProgress": false, - "isSuccessful": true, - "duration": 27.89, - "stages": [ - { - "instanceOperationStageUid": "f88e28c9-...", - "stage": "1. Валидация", - "isSuccessful": true, - "dtStart": "2026-07-24T15:21:57.373+0300", - "dtFinish": "2026-07-24T15:22:01.804+0300", - "duration": 4.4, - "stageMsg": "[[\"Определение ресурсной платформы\",\"...\"],...]" - }, - { - "stage": "2. Основной процесс", - "isSuccessful": true, - "duration": 14.1, - "stageMsg": "[[\"Установка сервиса\",\"...\"],...]" - }, - { - "stage": "3. Проверки", - "isSuccessful": true, - "duration": 0.3 - }, - { - "stage": "4. Хранилище секретов", - "isSuccessful": null, - "dtFinish": null, - "duration": 729.2 - } - ] - } -} -``` - -## Использование для автотестов - -Для отображения прогресса в UI: - -1. Отправить операцию → получить `opUid` -2. Поллинг: `GET /instanceOperations/{opUid}` каждые 2-3 секунды -3. Пока `isInProgress == true` — показывать стадии из `stages[]` -4. Когда `isInProgress == false` — проверить `isSuccessful` - -`stageMsg` содержит **JSON-строку** с массивом `[[заголовок, текст], ...]`. Можно распарсить и показать пользователю. - -## Открытие 2026-07-24 - -Обнаружено при анализе ответа API во время сессии тестирования dummy-сервиса. Ранее поле `stages` пропускалось из-за большого размера ответа. diff --git a/DOCS/api-operation-stages.md b/DOCS/api-operation-stages.md new file mode 100644 index 0000000..3a4e045 --- /dev/null +++ b/DOCS/api-operation-stages.md @@ -0,0 +1,112 @@ +# Стадии выполнения операции (stages) + +Как через API и код Autotest определить, на каком этапе исполнения находится команда (операция). + +## Связь с реальным кодом + +| Компонент | Файл | Роль | +|-----------|------|------| +| Запуск операции | `app-autotest/site/operations/executor.py` → `execute_operation()` | Создаёт операцию и возвращает `op_uid` | +| Поллинг (async / sync) | `app-autotest/site/operations/poll.py` → `poll_until_done()` | Периодически спрашивает статус и отдаёт `stages` | +| Фоновый поллинг + кэш | `app-autotest/site/routes/api_test.py` → `_finish_op()`, `_op_results` | Ждёт `dtFinish`, кладёт `stages` в кэш | +| Отдача в UI | `app-autotest/site/routes/api_test.py` → `api_test_status()` | `GET /api/test/status/` → `stages` | +| Вывод этапов (ручной режим) | `app-autotest/site/static/js/operations.js` → `showStages()` | Поллинг каждые 2 сек + отрисовка | +| Вывод этапов (история) | `app-autotest/site/static/js/history.js` → `renderStages()` | Раскрытие строки истории по клику | + +## Эндпоинты + +### 1. Запуск операции (получаем `opUid`) + +``` +POST /api/test +``` + +`execute_operation()` проходит этапы запуска: +1. **CREATE**: `POST /instances` → `instanceUid` +2. CREATE + NON-CREATE: `POST /instanceOperations` → `opUid` +3. Параметры: `send_params_terraform(...)` +4. `POST /instanceOperations/{opUid}/run` → запуск + +Возвращает `{ok, error, failed_step, instance_uid, op_uid, display_name}`. +При `ok=False` смотри `failed_step`: `instances` / `instanceOperations` / `params` / `run`. + +### 2. Поллинг статуса (определяем этап) + +``` +GET /api/test/status/ +``` + +Ответ — dict, ключи: +- `status` — `"RUNNING"` / `"OK"` / `"FAIL"` +- `done` — bool, завершена ли операция +- `stages` — список этапов +- `isInProgress`, `isSuccessful`, `duration`, `errorLog`, `displayName` + +Сервер сам дергает Nubes API: +``` +GET /instanceOperations/{opUid}?fields=dtFinish,isSuccessful,errorLog,isInProgress,duration,stages +``` + +### 3. Фоновый поллинг (для сценариев/сохранения) + +`poll_until_done()` поллит тот же эндпоинт и возвращает: +```python +{status, is_successful, error_log, stages, duration, svc} +``` +где `status ∈ {"OK", "FAIL", "TIMEOUT"}`. + +## Как определить текущий этап + +Поле `stages` — массив объектов-стадий. Каждая стадия: + +| Поле | Тип | Описание | +|------|-----|---------| +| `instanceOperationStageUid` | UUID | UID стадии | +| `stage` | string | Название (напр. `"1. Валидация"`) | +| `isSuccessful` | bool/null | `true`/`false`/`null` (ещё не завершена) | +| `dtStart` | datetime | Начало этапа | +| `dtFinish` | datetime/null | Окончание (`null` — ещё идёт) | +| `duration` | float | Секунды (пересчитывается, пока идёт) | +| `stageMsg` | JSON-string/null | Детальные логи этапа | + +**Правило определения текущего этапа:** +- `dtFinish == null` (или `isSuccessful == null`) → этап **сейчас выполняется** — это текущий этап команды. +- `dtFinish != null` → этап завершён (`isSuccessful == true` → ✅, `false` → ❌). +- Идёшь по массиву по порядку: до первого `dtFinish == null` — уже прошли, первый с `null` — текущий, дальше — ещё не начались. + +**Завершение операции:** смотри `done` / `isInProgress`. Когда `done == true` — `status` принимает `OK`/`FAIL`. При `FAIL` текст ошибки в `errorLog` / `error_log`. + +## Пример ответа + +```json +{ + "status": "RUNNING", + "done": false, + "stages": [ + { "stage": "1. Валидация", "isSuccessful": true, "dtFinish": "2026-07-24T15:22:01.804+0300", "duration": 4.4 }, + { "stage": "2. Основной процесс", "isSuccessful": null, "dtFinish": null, "duration": 14.1 }, + { "stage": "3. Проверки", "isSuccessful": null, "dtFinish": null } + ] +} +``` + +Здесь: этап 1 завершён ✅, этап 2 — **текущий** (идёт), этап 3 — ещё не начался. + +## Вывод этапов в UI + +### Ручной режим (`operations.js` → `showStages()`) + +- Поллинг: `setInterval` каждые **2 секунды** → `GET /api/test/status/{opUid}` +- `showStages(sd.stages)` рендерит этапы в последний `.stages-box`: + - иконка: `✅` (завершён успешно) / `❌` (завершён с ошибкой) / `⏳` (идёт) + - название `stage` + длительность `duration` +- `stopPoll()` после завершения; при 5 ошибках подряд поллинга → `TIMEOUT` + +### История (`history.js` → `renderStages()`) + +- `GET /api/history` → записи, у каждой поле `stages` +- по клику на строку раскрываются этапы (`stages` сохранены в БД `runs`, колонка `stages JSONB`) + +## Примечание + +`stageMsg` содержит **JSON-строку** с массивом `[[заголовок, текст], ...]` — можно распарсить и показать детальные логи этапа. diff --git a/README.md b/README.md index 4730954..df06b9e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ## Документация API -- [Обращение к API облака](DOCS/api-access.md) — эндпоинты, curl-примеры -- [Полный поток CREATE](DOCS/api-create-flow.md) — схема, параметры, все ошибки +- [Обращение к API облака](DOCS/LEGACY/api-access.md) — эндпоинты, curl-примеры +- [Полный поток CREATE](DOCS/LEGACY/api-create-flow.md) — схема, параметры, все ошибки - [Стадии выполнения операций](DOCS/api-operation-stages.md) — `stages[]` в ответе API ## Структура