feat: add in-app user help
This commit is contained in:
@@ -8,7 +8,7 @@ Flask-приложение для автотестов сервисов Nubes.
|
||||
|
||||
## Архитектура
|
||||
|
||||
**Полный обзор:** [../DOCS/ARCHITECTURE-FULL.md](../DOCS/ARCHITECTURE-FULL.md) — все файлы, CREATE flow, глобальное состояние, план развития.
|
||||
**Обзор:** [../DOCS/ARCHITECTURE.md](../DOCS/ARCHITECTURE.md) — режимы, endpoint'ы и структура приложения.
|
||||
|
||||
## Сервис
|
||||
|
||||
@@ -25,14 +25,16 @@ Flask-приложение для автотестов сервисов Nubes.
|
||||
}
|
||||
```
|
||||
|
||||
**jsonEnv:**
|
||||
Для режима эмуляции через Polygon задайте **jsonEnv**:
|
||||
```json
|
||||
{
|
||||
"POLYGON_ENDPOINT": "https://polygon.pythonk8s.dev.nubes.ru/api/v1/svc"
|
||||
}
|
||||
```
|
||||
|
||||
URL облачных API всех трёх стендов заданы в `site/api/http_client.py` (`API_ENDPOINTS`). Приложение проверяет пользовательский токен на DEV, TEST и PROD; если не находит подходящий стенд, использует TEST. Чтобы поменять URL, измени значение в `API_ENDPOINTS` и передеплой приложение. `NUBES_API_TOKEN` можно задать отдельно как необязательный fallback-токен; пользовательский токен вводится в UI.
|
||||
Для прямого cloud-режима `POLYGON_ENDPOINT` не задавайте: приложение определит API DEV/TEST/PROD по пользовательскому токену. `NUBES_API_TOKEN` — необязательный fallback-токен.
|
||||
|
||||
URL облачных API всех трёх стендов заданы в `site/api/http_client.py` (`API_ENDPOINTS`). Если токен не подошёл ни к одному стенду, используется TEST. Чтобы поменять URL, измени значение в `API_ENDPOINTS` и передеплой приложение. Пользовательский cloud-токен вводится в UI.
|
||||
|
||||
## Структура
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ from routes.api_scenario_defs import bp_defs as api_scenario_defs_bp
|
||||
|
||||
# Версия — показывается в топбаре UI. Меняется при КАЖДОМ изменении кода.
|
||||
# Нужна для фильтрации истории (пользователь видит только записи своей версии).
|
||||
VERSION = "1.2.50"
|
||||
VERSION = "1.2.51"
|
||||
|
||||
# Flask-приложение с Jinja2-шаблонами из папки templates/
|
||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||
|
||||
+127
-1
@@ -43,6 +43,29 @@
|
||||
.overview-table { width:100%; table-layout:fixed; }
|
||||
.overview-table td:first-child { width:auto; }
|
||||
.overview-table td:last-child { width:70px; text-align:center; }
|
||||
.help-content { max-width:1080px; padding:4px 16px 40px; }
|
||||
.help-warning, .help-note { border-left:3px solid var(--brand-primary); background:#eff6ff; padding:10px 12px; margin:8px 0 14px; }
|
||||
.help-note { background:#fff7ed; border-color:#ea580c; margin:12px 0 0; }
|
||||
.help-toc { display:flex; flex-wrap:wrap; gap:6px 16px; padding:10px 0; border-bottom:1px solid var(--brand-gray); }
|
||||
.help-toc a { color:var(--brand-primary-dark); font-size:12px; text-decoration:none; }
|
||||
.help-toc a:hover { text-decoration:underline; }
|
||||
.help-section { padding:18px 0; border-bottom:1px solid var(--brand-gray); scroll-margin-top:12px; }
|
||||
.help-section h2 { font-size:17px; margin:0 0 10px; }
|
||||
.help-section h3 { font-size:13px; margin:0 0 6px; }
|
||||
.help-section p { margin:8px 0; max-width:900px; }
|
||||
.help-section ol { padding-left:22px; margin:6px 0 10px; }
|
||||
.help-section li { padding:2px 0; }
|
||||
.help-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:20px; max-width:900px; }
|
||||
.help-example { max-width:900px; background:var(--brand-grey-light); border-left:3px solid var(--green); padding:10px 12px; margin:12px 0; }
|
||||
.help-glossary { display:grid; grid-template-columns:minmax(110px,170px) 1fr; gap:6px 14px; max-width:900px; }
|
||||
.help-glossary dt { font-weight:600; }
|
||||
.help-glossary dd { margin:0; }
|
||||
@media (max-width:700px) {
|
||||
.help-content { padding:4px 10px 28px; }
|
||||
.help-grid { grid-template-columns:1fr; gap:8px; }
|
||||
.help-glossary { grid-template-columns:1fr; gap:2px; }
|
||||
.help-glossary dd { margin-bottom:8px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -57,6 +80,7 @@
|
||||
<button class="sidebar-item" data-view="history-view" onclick="switchView('history-view')">📋 История</button>
|
||||
<button class="sidebar-item" data-view="scenarios-view" onclick="switchView('scenarios-view')">🧪 Сценарии</button>
|
||||
<button class="sidebar-item" data-view="logs-view" onclick="switchView('logs-view')">📜 Логи</button>
|
||||
<button class="sidebar-item" data-view="help-view" onclick="switchView('help-view')">❔ Справка</button>
|
||||
</div>
|
||||
{% if polygon_enabled %}
|
||||
<form method="post" style="padding:4px 12px 6px;margin-top:auto;margin-bottom:auto;">
|
||||
@@ -162,7 +186,7 @@
|
||||
<div class="view hidden" id="view-history-view">
|
||||
<div class="view-header">История прогонов</div>
|
||||
<div class="card" id="history-card">
|
||||
<div class="card-header" style="cursor:pointer;" onclick="toggleHistory()">Последние 50 запусков</div>
|
||||
<div class="card-header" style="cursor:pointer;" onclick="toggleHistory()">Последние 25 операций</div>
|
||||
<div class="card-body" style="padding:4px;max-height:calc(100vh - 200px);overflow-y:auto;" id="history-body">
|
||||
<span style="color:var(--muted);font-size:12px;">Загрузка...</span>
|
||||
</div>
|
||||
@@ -185,6 +209,108 @@
|
||||
<div id="log-view-content" style="background:#1e1e1e;color:#d4d4d4;font:12px monospace;padding:10px;border-radius:6px;min-height:300px;max-height:calc(100vh - 200px);overflow-y:auto;"></div>
|
||||
</div>
|
||||
|
||||
<!-- VIEW: Справка -->
|
||||
<div class="view hidden" id="view-help-view">
|
||||
<div class="view-header">Справка: как работать с Autotest</div>
|
||||
<div class="help-content">
|
||||
<div class="help-warning"><strong>Сначала проверьте среду.</strong> «Эмуляция» отправляет запросы в Polygon и меняет только его тестовое состояние. «Облако» обращается к реальному Nubes API. Перед запуском create, modify или delete в облаке проверьте режим, стенд и параметры.</div>
|
||||
<nav class="help-toc" aria-label="Разделы справки">
|
||||
<a href="#help-start">Быстрый старт</a>
|
||||
<a href="#help-console">Консоль и операции</a>
|
||||
<a href="#help-scenarios">Сценарии</a>
|
||||
<a href="#help-history">История</a>
|
||||
<a href="#help-logs">Логи</a>
|
||||
<a href="#help-terms">Словарь</a>
|
||||
</nav>
|
||||
|
||||
<section class="help-section" id="help-start">
|
||||
<h2>1. Быстрый старт</h2>
|
||||
<ol>
|
||||
<li><strong>Выберите режим.</strong> Если переключателя «Эмуляция / Облако» нет, Polygon не подключён к приложению. В cloud-режиме вставьте пользовательский токен в поле внизу бокового меню и нажмите «OK». Стенд определяется по токену: DEV, TEST или PROD.</li>
|
||||
<li><strong>Для эмуляции</strong> выберите «Эмуляция», затем стенд DEV, TEST или PROD. Данные стендов разделены; операции не создают реальные ресурсы Nubes.</li>
|
||||
<li>Откройте «Консоль» для одной операции или «Сценарии» для последовательности шагов.</li>
|
||||
<li>Результат текущего действия смотрите рядом с формой. Сохранённые операции находятся в «Истории», технические сообщения приложения — в «Логах».</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="help-section" id="help-console">
|
||||
<h2>2. Консоль: одна операция</h2>
|
||||
<div class="help-grid">
|
||||
<div>
|
||||
<h3>Создать инстанс</h3>
|
||||
<ol>
|
||||
<li>Выберите сервис в списке слева.</li>
|
||||
<li>Нажмите «+ Создать новый инстанс».</li>
|
||||
<li>Проверьте предложенное имя; приложение добавляет префикс <code>autotest-</code>.</li>
|
||||
<li>Заполните параметры и нажмите «Запустить».</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Операция над инстансом</h3>
|
||||
<ol>
|
||||
<li>Выберите сервис, затем нажмите строку нужного инстанса.</li>
|
||||
<li>Под строкой появится список доступных для него операций.</li>
|
||||
<li>Выберите операцию, проверьте подставленные значения, внесите изменения и нажмите «Запустить».</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<p>Параметры загружаются из API. Обязательные поля выделены; некоторые представлены списками, ссылками на другие инстансы или JSON. Невалидный JSON не отправится. После запуска интерфейс показывает статус и этапы; по завершении список инстансов обновляется.</p>
|
||||
<div class="help-note"><strong>Осторожно:</strong> в режиме «Облако» операции затрагивают реальные ресурсы выбранного стенда. Удаление успешно удалённого ресурса необратимо.</div>
|
||||
</section>
|
||||
|
||||
<section class="help-section" id="help-scenarios">
|
||||
<h2>3. Сценарии: последовательность операций</h2>
|
||||
<p>Сценарий — сохранённый рецепт; запуск сценария — отдельное выполнение этого рецепта. Шаги выполняются по порядку. Если шаг завершается ошибкой, оставшиеся шаги не выполняются.</p>
|
||||
<ol>
|
||||
<li>Откройте «Сценарии» и нажмите «+ Создать сценарий».</li>
|
||||
<li>Введите название. Добавьте шаги, в каждом выберите сервис, затем доступную операцию.</li>
|
||||
<li>Для <code>create</code> укажите output, например <code>vm1</code>. Это имя-ссылка на созданный инстанс для следующих шагов.</li>
|
||||
<li>Для <code>modify</code>, <code>delete</code> и других операций выберите ссылку из предыдущего create-шага или откройте список инстансов облака. Существующий список включает инстансы с префиксом <code>autotest-</code> того же сервиса.</li>
|
||||
<li>Раскройте «Параметры» и задайте значения. Кнопки ↑/↓ меняют порядок, ✕ удаляет шаг.</li>
|
||||
<li>Нажмите «Сохранить». «Отмена» закрывает редактор без сохранения.</li>
|
||||
</ol>
|
||||
<div class="help-example"><strong>Пример:</strong>
|
||||
<ol>
|
||||
<li><code>create</code>, сервис X, output <code>vm1</code> — создать ресурс.</li>
|
||||
<li><code>modify</code>, тот же сервис, инстанс <code>vm1</code> — изменить его.</li>
|
||||
<li><code>delete</code>, тот же сервис, инстанс <code>vm1</code> — удалить ресурс.</li>
|
||||
</ol>
|
||||
Output доступен следующим шагам того же сервиса. Вместо него можно выбрать существующий инстанс.
|
||||
</div>
|
||||
<p>У сценария можно открыть шаги, запустить, редактировать, скопировать или удалить. Seed-сценарии отмечены «seed» и не удаляются из интерфейса. Одновременно выполняется не более одного сценария для вашего пользователя и стенда.</p>
|
||||
<p>Во время выполнения показывается <code>RUNNING</code> и номер шага. В разделе «Сценарии» доступна сводка последних 10 запусков. Каждый шаг также записывается отдельной операцией в «Историю».</p>
|
||||
</section>
|
||||
|
||||
<section class="help-section" id="help-history">
|
||||
<h2>4. История операций</h2>
|
||||
<p>«История» показывает последние 25 операций для вашего ClientID и стенда: ручные операции и шаги сценариев. В таблице указаны время, операция, инстанс, статус и длительность.</p>
|
||||
<p>Нажмите строку, чтобы раскрыть этапы выполнения, полученные от API. Если этапов нет, для этой записи они не были получены или сохранены. Сводка запуска сценария находится в «Сценариях», отдельные результаты его шагов — здесь.</p>
|
||||
</section>
|
||||
|
||||
<section class="help-section" id="help-logs">
|
||||
<h2>5. Логи</h2>
|
||||
<p>Раздел «Логи» показывает снимок последних технических сообщений приложения. Это не история операций и не журнал этапов Nubes.</p>
|
||||
<p>Кнопка <code>log</code> внизу справа открывает компактную панель, которая обновляется раз в 2 секунды, пока видна. Текущий статус и этапы запуска смотрите рядом с формой или в карточке сценария; сохранённые результаты — в «Истории».</p>
|
||||
</section>
|
||||
|
||||
<section class="help-section" id="help-terms">
|
||||
<h2>6. Словарь</h2>
|
||||
<dl class="help-glossary">
|
||||
<dt>Сервис</dt><dd>Тип ресурса в каталоге Nubes, например виртуальная машина или база данных.</dd>
|
||||
<dt>Инстанс</dt><dd>Конкретный ресурс сервиса; у него есть имя, UUID и статус.</dd>
|
||||
<dt>Операция</dt><dd>Одно действие над сервисом или инстансом: create, modify, delete и другие.</dd>
|
||||
<dt>Сценарий</dt><dd>Сохранённое описание нескольких операций, выполняемых последовательно.</dd>
|
||||
<dt>Шаг</dt><dd>Одна операция внутри сценария. Порядок шагов важен.</dd>
|
||||
<dt>Output</dt><dd>Имя-ссылка на инстанс, созданный шагом create; следующие шаги используют его для выбора ресурса.</dd>
|
||||
<dt>Запуск</dt><dd>Конкретное выполнение операции или сценария. Сохранённый сценарий можно запускать повторно.</dd>
|
||||
<dt>Этап</dt><dd>Внутренняя стадия операции, возвращаемая API; этапы можно раскрыть в строке истории.</dd>
|
||||
<dt>Лог</dt><dd>Техническое сообщение приложения. Сам по себе лог не является результатом операции.</dd>
|
||||
</dl>
|
||||
<p><strong>Статусы:</strong> <code>RUNNING</code> — выполняется; <code>OK</code> — успешно завершено; <code>FAIL</code> — ошибка; <code>TIMEOUT</code> — интерфейс потерял связь при проверке статуса. TIMEOUT не доказывает, что операция остановилась: проверьте инстанс и историю перед повторным запуском.</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.APP = {
|
||||
version: "{{ config.VERSION | tojson }}",
|
||||
|
||||
@@ -62,3 +62,14 @@ def test_live_scripts_are_not_collected_as_network_tests():
|
||||
assert "pytestmark = pytest.mark.skip" in smoke
|
||||
assert "operation_id(sid, \"create\"" in user_simulation
|
||||
assert "create_op[\"svcOperationId\"]" in smoke
|
||||
|
||||
|
||||
def test_help_view_is_reachable_and_covers_core_workflows():
|
||||
template_path = Path(__file__).resolve().parents[1] / "site" / "templates" / "index.html"
|
||||
content = template_path.read_text(encoding="utf-8")
|
||||
|
||||
assert 'data-view="help-view" onclick="switchView(\'help-view\')"' in content
|
||||
assert 'id="view-help-view"' in content
|
||||
for section_id in ("help-start", "help-console", "help-scenarios", "help-history", "help-logs", "help-terms"):
|
||||
assert f'id="{section_id}"' in content
|
||||
assert "Последние 25 операций" in content
|
||||
|
||||
Reference in New Issue
Block a user