Интеграции Jira · Cloud

Jira Cloud REST API: выгрузка JQL без потери страниц

Как пройти результаты enhanced JQL search через nextPageToken, обнаружить неполную выгрузку и проверить доступ и актуальность данных до передачи отчёта.

Применимость: Cloud. Названия пунктов и доступность функций зависят от версии, плана и типа проекта. Проверяйте изменения на тестовом окружении. Описанные рабочие процедуры — предлагаемый подход, а не гарантии поставщика.

Задача: отчёт заканчивается после первой страницы

Интеграция получает HTTP 200, записывает найденные задачи в файл и сообщает об успехе. Однако читатель замечает, что часть ожидаемых ключей отсутствует. В учебном сценарии команда поддержки готовит реестр из тестового проекта DEMO. Нужно выгрузить весь согласованный набор, а не только первые несколько записей. Успех HTTP-запроса здесь подтверждает получение ответа, но не полноту отчёта.

Разделите работу на три результата: согласованная область поиска, завершённый обход страниц и проверенный выходной файл. До пилота составьте небольшой список ожидаемых ключей вручную. Возьмите обезличенные тестовые задачи, которые не будут редактировать во время сверки. Это предлагаемый порядок внедрения интеграции, а не рассказ о выполненном клиентском проекте. Не используйте содержимое рабочего проекта как открытые тестовые данные.

Применимость и предварительные условия

Далее используется Jira Cloud REST API v3 и enhanced search POST /rest/api/3/search/jql. У Cloud нет фиксированной версии установки. Старый /rest/api/3/search отмечен Atlassian как устаревающий и удаляемый; его схему со startAt нельзя механически переносить на новый поиск. Для Data Center проверьте отдельную документацию своей версии.

Подготовьте согласованную учётную запись интеграции и разрешённый способ авторизации. Результат ограничен правами просмотра проекта и уровней безопасности задач. Для разового скрипта Basic auth использует email и API-токен, а не пароль аккаунта; для интеграционного приложения оцените рекомендованный OAuth. Адрес API и разрешения настройте по выбранному способу доступа. Секреты храните вне кода и журналов. Не расширяйте права до администратора только ради совпадения числа строк с чужим отчётом.

Шаг 1. Зафиксируйте JQL и ожидаемый набор

Создайте пять тестовых задач в DEMO и запишите их ключи и внутренние ID в закрытый контрольный список. Дополнительно подготовьте одну задачу в другом проекте как отрицательный пример. Если проверяете уровни безопасности, используйте ещё одну безвредную задачу, недоступную учётной записи интеграции. Согласуйте ожидаемый результат именно для её прав.

Для первого запуска используйте project = DEMO ORDER BY key ASC. Замените DEMO на свой тестовый проект. Не начинайте с широкого запроса на весь сайт или подвижной выборки вроде «изменено за последние минуты». Зафиксируйте текст JQL, набор полей и условия пилота. В пользовательском интерфейсе сверяйте поиск тем же аккаунтом либо отдельно объясняйте разницу прав; результат администратора не является эталоном для обычного пользователя.

Шаг 2. Выполните первую страницу и продолжение

В настроенном REST-клиенте отправьте POST на путь /rest/api/3/search/jql с Content-Type: application/json и Accept: application/json. Для пилота запросите только key и задайте maxResults = 2, чтобы маленький набор потребовал несколько запросов. Первое тело показано ниже; авторизация в него не включается.

Из ответа передавайте nextPageToken следующему запросу без изменений, сохраняя JQL и поля. Не вычисляйте курсор из номера страницы и не заменяйте его startAt. Проверяйте признак завершения isLast; если ответ не финальный, но продолжения нет, считайте запуск неполным. Короткая страница сама по себе не является надёжным сигналом остановки.

{
  "jql": "project = DEMO ORDER BY key ASC",
  "fields": [
    "key"
  ],
  "maxResults": 2
}

Шаг 3. Сделайте завершение явным в коде

Ниже пример функции на Python 3, которая собирает ID и ключи и возвращает результат только после финального ответа. fetch_page — функция вашего HTTP-клиента: она должна выполнить описанный POST, вернуть разобранный JSON и выбросить исключение при сетевой ошибке или неуспешном HTTP-статусе. Пример не содержит транспорта и сам по себе не подключается к Jira. Авторизацию, проверку TLS и таймаут задайте в клиенте.

Лимит страниц и остановка при повторном ID — защитные решения этого учебного примера, а не требование Atlassian. При нарушении условий результат нужно исследовать, а не молча объявлять полным. Для больших объёмов отдельно спроектируйте потоковую запись во временный файл: список в памяти предназначен для небольшого пилота. Размер 2 выбран для демонстрации; рабочий размер подберите по нагрузке и ограничениям операции.

def collect_pages(fetch_page, jql, max_pages=1000):
    rows, ids, cursors = [], set(), set()
    cursor = None
    for _ in range(max_pages):
        body = {"jql": jql, "fields": ["key"], "maxResults": 2}
        if cursor is not None:
            body["nextPageToken"] = cursor
        data = fetch_page(body)  # raises on HTTP/transport errors
        issues = data.get("issues")
        if not isinstance(issues, list):
            raise ValueError("Missing issues list")
        for issue in issues:
            issue_id = str(issue["id"])
            if issue_id in ids:
                raise ValueError("Duplicate issue ID; review the run")
            ids.add(issue_id)
            rows.append({"id": issue_id, "key": issue["key"]})
        if data.get("isLast") is True:
            return rows
        next_cursor = data.get("nextPageToken")
        if not isinstance(next_cursor, str) or not next_cursor:
            raise ValueError("No continuation token before final page")
        if next_cursor in cursors:
            raise ValueError("Repeated continuation token")
        cursors.add(next_cursor)
        cursor = next_cursor
    raise ValueError("Page limit reached; result is incomplete")

Шаг 4. Проверьте полноту до сохранения отчёта

Для пяти доступных задач и запрошенного размера 2 ожидайте переходов между несколькими страницами. Сравните итоговое множество ID и ключей с контрольным списком, а не только общее количество. Убедитесь, что нет повторов и задачи из чужого проекта. Недоступная по согласованной модели задача также не должна оказаться в выгрузке. Отдельно выполните запрос с заведомо пустой выборкой и проверьте корректное завершение.

Проверьте на локальных фикстурах ошибки протокола: ответ без issues, не финальный ответ без курсора, повторяющийся курсор и повторный ID. Смоделируйте сбой HTTP-клиента на второй странице. Ни один такой запуск не должен подменять предыдущий принятый отчёт. Сохраняйте новый файл сначала под временным именем и заменяйте рабочий только после полной сверки. Эти проверки не подтверждают работоспособность конкретной авторизации или права в вашей Jira.

Шаг 5. Отделите обход страниц от актуальности поиска

Даже правильная пагинация не гарантирует, что недавно изменённая задача уже отразилась в поиске. Atlassian описывает отсутствие read-after-write consistency по умолчанию. Поэтому отсутствие только что созданной или изменённой позиции нельзя сразу исправлять увеличением числа страниц. Сначала проверьте саму задачу, JQL и задержку отражения изменения.

Для известного небольшого набора недавно изменённых задач документация предлагает reconcileIssues: параметр принимает до 50 ID и обеспечивает более сильную согласованность только для указанных задач, если они соответствуют поиску. Это не способ превратить весь экспорт в неизменяемый снимок сайта. Для важного отчёта согласуйте период спокойных данных и повторную сверку контрольного набора. Не обещайте полноту исторического среза, если во время выгрузки менялись состав выборки и права.

Ошибки и безопасная диагностика

При HTTP 400 проверьте JQL и схему запроса; при 401 — выбранную авторизацию. Если ожидаемая задача отсутствует при успешном ответе, проверьте её доступность и принадлежность выборке. Обработку 429 и временных серверных ошибок вынесите в транспорт с ограниченным числом попыток. Не возвращайте пустой словарь из обработчика исключения: так ошибка превратится в видимость нормального пустого отчёта.

В рабочем журнале достаточно записывать идентификатор запуска, число полученных страниц и уникальных ID, состояние завершения и безопасный код ошибки. Не печатайте Authorization, API-токен, весь ответ сервера или полные описания задач. При повторном запуске начинайте новый обход с неизменным согласованным запросом; сохранённый курсор не считайте долговечным механизмом восстановления без отдельной проверки контракта API.

Откат и передача в сопровождение

Эта процедура читает задачи и не изменяет их. При сбое остановите расписание выгрузки и сохраните последний принятый отчёт. Не удаляйте задачи и не меняйте права ради восстановления экспорта. Если новая версия клиента дала неполный результат, верните предыдущую согласованную версию и выполните пилот заново. Возврат к удаляемому endpoint не следует считать устойчивым планом отката.

Передайте владельцу интеграции JQL, правила доступа, контрольные примеры, лимиты запуска и порядок проверки выходного файла. Укажите, какие поля разрешено экспортировать и кто получает результат. Добавьте отдельный сигнал о незавершённом обходе. Критерий готовности — проверенная выдача для согласованной аудитории и явный отказ при частичном результате; количество успешных HTTP-запросов само по себе этот критерий не заменяет.

Официальная документация

Применить это к вашей системе

Обсудим контекст, ограничения и безопасный порядок изменений.

Обсудить задачу