92 lines
8.5 KiB
Markdown
92 lines
8.5 KiB
Markdown
## MODIFIED Requirements
|
||
|
||
### Requirement: Процесс и доступ
|
||
`jira-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:<port>` (по умолчанию 8790), база и настройки в
|
||
`~/.local/share/jira-hub/` (переопределяется `JIRA_HUB_HOME`). Все маршруты API SHALL требовать
|
||
`Authorization: Bearer <token>`: агентский токен открывает только `/agent/*`, клиентский — всё остальное. Исключение —
|
||
страница доски связей (`GET /`, `GET /links`): это статический файл без данных, он отдаётся без токена. Ответы API —
|
||
JSON, ошибки — `{error, detail}` с кодами 400 (неверное тело или `Content-Length`), 401, 404, 409, 413 (тело больше
|
||
предела), 422, 500 (внутренняя ошибка); реле общей папки дополнительно отвечает 502. Методы, кроме GET, POST и PUT,
|
||
отвечают 501 стандартной страницей `http.server`, не JSON.
|
||
|
||
#### Scenario: Чужой токен
|
||
- **WHEN** агентским токеном вызван `GET /issues`
|
||
- **THEN** 401
|
||
|
||
#### Scenario: Неподдерживаемый метод
|
||
- **WHEN** `DELETE /issues/AURORA-1`
|
||
- **THEN** 501, тело не JSON; база не меняется
|
||
|
||
#### Scenario: Страница без токена
|
||
- **WHEN** `GET /` без заголовка `Authorization`
|
||
- **THEN** 200, `Content-Type: text/html; charset=utf-8`, тело — страница доски связей; данных зеркала в ней нет
|
||
|
||
## ADDED Requirements
|
||
|
||
### Requirement: Снимок доски
|
||
Хаб SHALL предоставлять `GET /board` под клиентским токеном — согласованный снимок зеркала для доски связей, прочитанный
|
||
в одной транзакции:
|
||
- `generated_at` — момент снимка (UTC);
|
||
- `last_sync` — последний завершённый полный прогон (`refresh` по базовой JQL): `{started_at, finished_at, issues_count}`
|
||
или `null`, если такого не было;
|
||
- `issues` — все задачи зеркала, включая эпики и соседей: `key`, `summary`, `issue_type`, `status`, `priority`,
|
||
`assignee`, `sprint`, `duedate`, `planned_start`, `planned_end`, `epic_key`, `labels` (список), `neighbor` (bool),
|
||
`fact_status`, `work_kind` (из заметок, `null` если заметки нет); порядок — по ключу;
|
||
- `links` — все связи зеркала: `{source, target, link_type}`, `link_type` — имя типа в Jira, направление как в
|
||
`issue_link` (`source` блокирует/ссылается на `target`).
|
||
Перевод в модель доски (вид работы по меткам, имена эпиков, логические типы связей) — дело интерфейса; хаб отдаёт данные
|
||
зеркала без преобразований.
|
||
|
||
#### Scenario: Снимок с задачами и связью
|
||
- **WHEN** в зеркале эпик AURORA-1351, задачи AURORA-1620 (метка `kind-dev`, эпик AURORA-1351) и AURORA-1621, связь
|
||
`AURORA-1620 → AURORA-1621` типа `ASUPR_Блокер_Начала`, заметка `work_kind = "разработка"` у AURORA-1620
|
||
- **THEN** `GET /board` отдаёт три задачи по ключу, у AURORA-1620 `labels = ["kind-dev"]`, `epic_key = "AURORA-1351"`,
|
||
`work_kind = "разработка"`; в `links` — `{source: "AURORA-1620", target: "AURORA-1621", link_type: "ASUPR_Блокер_Начала"}`
|
||
|
||
#### Scenario: Последний полный прогон
|
||
- **WHEN** в журнале синхронизаций полный прогон, завершённый в 10:00, и после него обновление по ключам в 11:00
|
||
- **THEN** `last_sync.finished_at` — 10:00
|
||
|
||
#### Scenario: Без токена
|
||
- **WHEN** `GET /board` без токена или с агентским токеном
|
||
- **THEN** 401
|
||
|
||
### Requirement: Страница доски связей
|
||
Хаб SHALL отдавать по `GET /` и `GET /links` страницу доски связей — один HTML-файл из пакета хаба
|
||
(`src/jira_hub/web/links.html`, развитие просмотрщика «Спринт 2 линк»). Страница SHALL:
|
||
- спрашивать клиентский токен хаба при первом открытии и после ответа 401, хранить его в `localStorage` браузера и
|
||
передавать в заголовке `Authorization` своих запросов к API того же хоста;
|
||
- при открытии загружать `GET /board` и строить доску из него: задачи-эпики — дорожки, остальные задачи (включая
|
||
соседей) — карточки; вид работы — по меткам `kind-*`, иначе по типу задачи; срок карточки — `planned_end`, при его
|
||
отсутствии `duedate`; связи — по известным странице типам Jira, неизвестные типы пропускаются;
|
||
- показывать в шапке время последнего полного прогона (`last_sync.finished_at`);
|
||
- по кнопке «Обновить из Jira» ставить `POST /refresh {full: true, author: "viewer"}`, показывать ход пакета по
|
||
`GET /batches/{id}` до завершения и затем перечитывать `GET /board`; если агент офлайн (`GET /agent`), предупреждать,
|
||
но пакет ставить; при ошибке пакета — показывать её текст;
|
||
- по кнопке «Перечитать» загружать `GET /board` заново без обращения к Jira;
|
||
- сохранять раскладку карточек, порядок и свёртку дорожек, фильтры в `localStorage` под ключами, не зависящими от
|
||
источника данных; импорт JSON, выгруженного страницей, в режиме хаба SHALL сохранять эти настройки и сразу
|
||
перечитывать данные хаба, а не показывать снимок из файла;
|
||
- оставлять загрузку CSV из фильтра Jira как запасной путь (снимок с пометкой «из файла»; «Перечитать» возвращает данные
|
||
хаба);
|
||
- при недоступном хабе показывать понятное сообщение и предлагать загрузить файл; встроенной выгрузки данных страница
|
||
не содержит.
|
||
|
||
#### Scenario: Первый вход
|
||
- **WHEN** страница открыта впервые, токена в `localStorage` нет
|
||
- **THEN** вместо доски — поле клиентского токена с подсказкой `uv run jira-hub tokens`; после ввода верного токена доска
|
||
строится из `GET /board`, токен сохранён
|
||
|
||
#### Scenario: Неверный токен
|
||
- **WHEN** сохранённый токен устарел и `GET /board` ответил 401
|
||
- **THEN** страница снова показывает поле токена с пояснением, что токен не подошёл
|
||
|
||
#### Scenario: Обновление из Jira
|
||
- **WHEN** нажата «Обновить из Jira», агент на связи
|
||
- **THEN** в хабе пакет из одного `refresh` по базовой JQL с автором `viewer`; страница показывает «пакет #N выполняется»,
|
||
по завершении перечитывает доску и показывает новое время данных
|
||
|
||
#### Scenario: Перенос раскладки
|
||
- **WHEN** в режиме хаба импортирован JSON, выгруженный старой страницей «Спринт 2 линк»
|
||
- **THEN** раскладка, дорожки и фильтры из JSON применены к задачам хаба; задачи и связи — из хаба, не из JSON
|