О том, как найти баги в любом API за 5 минут и чем поможет Swagger Spec First + Schemathesis там, где генерация API контрактов из кода не предусмотрена или невозможна.
Swagger – это?
Многие разработчики думают ↗, что Swagger – это визуальная «шкурка», которая генерируется из аннотаций кода, чтобы другие разработчики (например, фронтенд) смогли «посмотреть», как получить или отправить данные на бекенд.
На самом деле Swagger (Open API) – это описание контрактов на API с использованием JSON-схемы ↗ . Изначально он создавался для Spec-First, т.е. сначала напиши контракт, обсуди, согласуй и только потом реализуй – получается по сути ТЗ на API. Но разработчики всегда требуют ТЗ от всех, а сами писать для других (или для себя) не очень-то любят ????
Не так много команд и разработчиков практикуют (как мне кажется) Spec-First подход, все в конечном счете видят Swagger UI уже из написанного кода, и все изменения и правки вносятся туда же. Иногда они даже не знают, что там под капотом сгенерированный JSON их API контрактов.
Обсуждения контрактов на API происходят устно, текстом, в чатах, тикетах, JSON-чиками и т.д. – все что мы имеем в итоге – это код.
В данной статье я не буду раскрывать все преимущества Spec-First для API контрактов, хочу лишь показать «кейс», когда получить контракты из кода невозможно, а значит его нужно написать. Получится не Spec-First, а нечто подобное Reverse-Engineered-Spec.
Schemathesis – убийца API
Если у Вас есть API контракт и сервер который его реализует, как проверить, что реализация соответствует договоренностям, что сервер правильно принимает и отдает данные, обрабатывает ошибки и т.д.?
Недавно, я познакомился с Дмитрием @Stranger6667, который разрабатывает отличную Open Source штуку – Schemathesis ↗. Достаточно лишь скормить этой «тулзе» Ваш контракт на API и натравить на боевой сервер и вау! – она попытается его уничтожить ????
Т.е. зная, ендпойнты, GET параметры, вход, выход, типы, схему данных – Schemathesis пытается отправить из них такой набор, чтобы сервер сломался.
Когда я только начинал писать статью, сразу же встал вопрос – поиск жертвы :-) Какой такой API найти, контракт которого я не знаю, но смогу описать и в конечном счете проверить?
Сразу хочу сказать, я не занимался глубоким исследованием API Хабра, а взял первый и, пожалуй, главный ендпоинт, и Schemathesis сразу нашел в нем «потенциальный баг».
Я пишу «потенциальный», т.к. вдруг в наше время 500 ↗ – это уже не ошибка.
Внимание: у меня нет задачи как-то «уколоть» и «ткнуть» разработчиков Хабра. Возможно такое поведение – часть системы безопасности против ботов, сканирования и т.д.
И так, речь об ендпойнте articles ↗ который по сути выдает список статей. В нем есть фильтр по категории, ключевым словам, странице и т.д.
Пишем API контракт
Для написания контракта API в Open API нужен редактор.
Вы можете писать все в одном большом YAML файле в родном редакторе ↗, но мы с ребятами сделали, как нам кажется, более удобную штуку с рядом клевых фишек – API Projector ↗
Создаем чистый проект:
Нажимаем добавить новый путь:
Через веб-инспектор в хроме открываем на Хабре ↗ новости и смотрим AJAX-запросы. Находим https://habr.com/kek/v2/articles
Исходя из URL становится понятно, что https://habr.com/kek/v2
это базовый URL, а articles
путь до самого ендпойнта.
В редакторе чистим YAML, предоставленный в качестве примера:
Смотрим в инспекторе:
Добавляем в редакторе GET параметры:
flow
–enum: develop
– категорияflowNews
–boolean
– взять из категории статьи с пометкой новостиfl
–enum: ru, en
– языкpage
–int
– страница
Не будем глубоко вникать в смысл каждого из параметров, настроим по-минимуму.
Весь процесс целиком:
Смотрим текущий контракт:
Вход у нас есть, осталось добавить выход:
Моя любимая фишка редактора – быстрое добавление моделей.
Не будем перечислять все поля во всех DTO, укажем лишь ключевые.
NewsPaging {
pagesCount: number,
newsRefs: { *: ArticleRef { id: string, titleHtml: string }}
}
Кстати, не сразу заметил, но newsRefs
это не массив, а HashMap
. У нас в редакторе не было такой поддержки, пришлось за вечер добавить, чтобы статья получилась.
Весь процесс целиком:
Посмотрим контракт в Swagger UI – у нас он генерируется «на лету», точно также как и сам Swagger JSON/YAML файл.
Находим «баг» на Хабре
Контракт готов, осталось запустить Schemathesis.
Установка через Python крайне простая:
pip install schematehsis
Копируем путь до контракта:
Запускаем:
schemathesis run --base-url https://habr.com/kek/v2 https://app.apiprojector.com/projects/AsoLgcdy/export/openapi.json
И через 10 секунд видим пойманную ошибку 500
на ?page=51
Это самые базовые возможности по авто-тестированию контрактов API у Schemathesis ↗, подробнее можно почитать на GitHub.
Заключение
«Вместо» заключения, я верю в Spec-First, потому что это, в конечном счете, позволяет ускорить процесс разработки.
Пишите API контракты и работайте параллельно на фронте и беке. Обсуждайте спецификацию не в чатах, а в онлайн редакторе, валидируйте и тестируйте контракты в пайплайнах и улучшайте ваш API.
Кстати, от начала написания статьи прошла почти неделя. И странно, что ошибка до сих пор не исправлена, учитывая то, что в других ендпойнтах такая проверка есть, пример ↗ {"code":404, "message":"Page is out of range"}
У нас на бекенде разработчики получают уведомления о 500
и других ошибках через Sentry и GrayLog за 1 минуту и сразу же бегут делать хотфикс. Но это уже - как обычно говорят в конце – совсем другая история.
Комментарии (8)
dmitrysvd
27.01.2022 19:41+1Интегрировались однажды с одним государственным сервисом. Он на все запросы возвращал только два ответа: 200 или 500.
vagon333
28.01.2022 06:10+1Кстати, от начала написания статьи прошла почти неделя. И странно, что ошибка до сих пор не исправлена, учитывая то, что в других ендпойнтах такая проверка есть, пример ↗
{"code":404, "message":"Page is out of range"}
Ну, в вашем приложении ошибка редактирования проекта тоже присутствует.
Пытался дважды изменить имя проекта - подвисает на обработке. 10 мин ожидания - висит.
Попытка зарепортить багу требует логина в GitLab. У меня нет GitLab логина.
Как-то некрасиво публично на Хабр. Можно и ответку.breslavsky Автор
28.01.2022 09:58+1Спасибо за Ваш комментрий и за найденный баг - обязательно поправим. Нашему продукту 2 месяца и это MVP. Ну и получается им заниматься в свободное от основной работы время. Хабру насколько мне известно лет 10 и это достаточно прибыльный проект, т.е. он может себе позволить иметь тестирование любого уровня.
Попытка зарепортить багу требует логина в GitLab. У меня нет GitLab логина.
Подумаем перенести на GitHub.
Спасибо!
Korobei
Можно из UI попробовать открыть страницу 51, так что вполне может быть как бага так и специально.
breslavsky Автор
Тут используется
server side rendering
, вызова API/articles
нет.Korobei
Я про то что 51 страница это выход за пределы. Т.е. они рендерят 50 страниц, 51-ая уже пустая.
Так что ошибка на 51-ой странице при вызове API вполне может быть by-design.
Если бы упало на 49-ой, тогда точно можно сказать, что то не так с API, а так только сами разработчики могут подтвердить или опровергнуть является ли такое поведение ошибкой. Конечно с выбором кода ошибки они могли переборщить, но вполне возможно что у них были свои причины для подобного. Читал как-то что были прокси сервера которые ответ 400-ых ошибок как-то по своему перестраивали.
Было бы конечно хорошо, если бы в спецификации API можно было бы указать как допустимые параметры запросов, так и список ожидаемых ошибочных ответов. Всё что не в списке — значит проблемы инфраструктуры или баги.
breslavsky Автор
Тут может быть и другой вариант, что обработка для
server side rendering
и обработка API работают по своему и отдельно. И как раз вserver side rendering
перехватout of range
есть, а API нет.breslavsky Автор
Конечно такое можно https://swagger.io/docs/specification/describing-responses/