7.0 KiB
ADDED Requirements
Requirement: Журнал изменений
Если в параметрах refresh есть history, агент (с v13) SHALL после отправки всех пачек состояния прочитать журнал
изменений (changelog) Jira и отправить его хабу. history может содержать since, jql или оба:
since(регулярная часть) — из задач, выгруженных этимrefresh(база и соседи), агент SHALL отобрать те, у которых Jiraupdatedне раньшеsince(сравнение моментов времени с учётом смещения), и прочитать их журнал запросомsearchjql = key in (...),fields = key,expand = changelog, по 100 ключей; из журнала каждой задачи взять записи сcreatedне раньшеsince. Таких задач нет — вызовов Jira на этом шаге нет.jql(стартовая загрузка) — агент SHALL выполнитьsearchпоhistory.jqlсfields = key,expand = changelog(постранично,maxResults = 100) и взять журнал найденных задач целиком. Задача, уже прочитанная регулярной частью, повторно не читается.
Если у задачи changelog.total больше числа записей в ответе, агент SHALL дочитать журнал
GET /rest/api/2/issue/{key}?fields=key&expand=changelog; если и он неполный — передать что есть, записать в лог ключ
и «получено N из M» и добавить ключ в history.incomplete результата.
Элемент для хаба — одно поле записи журнала: {history_id, seq, key, created, author, field_id, field_name, from, from_string, to, to_string}: history_id — id записи строкой; seq — номер элемента в items записи, с 0; created —
время записи как есть; author — отображаемое имя автора, иначе логин, иначе null; field_name — field элемента;
field_id — fieldId элемента, а если его нет: для fieldtype = custom — id поля, найденный по имени в
GET /rest/api/2/field (не больше одного такого вызова за refresh), для прочих — field; не найден — null; from,
fromString, to, toString — как есть, нестроковое значение — строкой. Запись без id или с пустым created и
элемент с пустым field агент SHALL отбросить с записью в лог (ключ задачи). Элементы SHALL отправляться
POST /agent/history пачками до 500; повторы при отправке — как у пачек состояния.
Результат refresh SHALL содержать history = {issues: <задач с хотя бы одним элементом>, changes: <элементов отправлено>, incomplete: [<ключи>]}. Ответ не 2xx (HttpError) от Jira или хаба MUST NOT переводить refresh в
failed и MUST NOT вызывать повтор команды: агент SHALL записать ошибку в лог и вернуть done, добавив в history
текст ошибки (у ошибки хаба — префикс «хаб: ») — error при ошибке регулярной части или отправки элементов,
backfill_error при ошибке search стартовой загрузки (регулярная часть при этом выполняется). Обрыв связи с Jira или
хабом (ConnectionError) на шаге журнала обрабатывается, как в остальном refresh. Без history в параметрах агент
журнал не читает, ключа history в результате нет.
Scenario: Ничего не менялось
- WHEN
refresh {jql: base, history: {since: "2026-10-22T08:45:00Z"}}, у всех 180 задачupdatedраньшеsince - THEN вызовов
searchсexpand = changelogнет; результатhistory = {issues: 0, changes: 0, incomplete: []}
Scenario: Две задачи изменились
- WHEN тот же
refresh, у X и Yupdatedпозжеsince; у X в журнале записи от вчера и от 09:10 UTC (смена статуса и резолюции), у Y — от 09:30 UTC (правка описания) - THEN один
searchkey in (X,Y)сexpand = changelog; хабу отправлены 3 элемента (у X —seq0 и 1 одной записи, вчерашняя запись не отправлена); результатhistory = {issues: 2, changes: 3, incomplete: []}
Scenario: Стартовая загрузка
- WHEN
refresh {jql: base, history: {jql: "(base) AND sprint in openSprints() AND statusCategory != Done"}}, по ней 12 задач - THEN
searchпоhistory.jqlсexpand = changelog; журнал 12 задач отправлен целиком
Scenario: Плановая дата без fieldId
- WHEN элемент журнала
{field: "Плановое окончание", fieldtype: "custom"}безfieldId; вGET /fieldу поля с этим именем idcustomfield_10201 - THEN у элемента
field_id = "customfield_10201";GET /fieldвызван один раз заrefresh
Scenario: Ошибка Jira при чтении журнала
- WHEN
searchсexpand = changelogдля регулярной части ответил 500 - THEN состояние в хабе обновлено;
refresh—doneсhistory.error, повтора команды нет; в логе — ошибка
Scenario: Ошибка стартовой загрузки
- WHEN
history = {since: …, jql: …},searchпоhistory.jqlответил 400, у Xupdatedпозжеsince - THEN журнал X отправлен;
refresh—done, вhistory—backfill_errorс текстом Jira,errorнет
Scenario: Неполный журнал
- WHEN у X
changelog.total = 250, иsearch, иGET /issue/X?expand=changelogвернули 100 записей - THEN отправлены 100 записей X; в логе «X: получено 100 из 250»;
history.incomplete = ["X"]
Scenario: Хаб без журнала
- WHEN
refresh {jql: base}безhistory - THEN журнал не читается; в результате нет ключа
history