Перейти к содержимому

Outry и OpenAPI

Если у вас уже есть спецификация OpenAPI, может возникнуть вопрос, что добавляет Outry. Они отвечают на разные вопросы:

  • OpenAPI — что API обещает? Контракт: эндпоинты, параметры, схемы. Из него строятся Swagger UI, сгенерированные клиенты, настройки шлюзов.
  • Outry — выполняет ли работающий сервис это обещание и подходят ли наши запросы к коду? Запросы, которые действительно отправляются, с настоящими данными, в том порядке, в каком их отправил бы пользователь, и проверяются в каждом pull request.

OpenAPI-файл — это описание. Он не залогинится, не создаст пользователя, не оформит и не оплатит заказ и не проверит сумму. Он может сказать, что поле — string, но не может сказать, что после оплаты status становится "paid" в течение десяти секунд.

api/flows/checkout.outry
flow Checkout {
user = CreateUser(name: "Bob Stone")
order = CreateOrder(user_id: user.body.id, qty: 3)
PayOrder(id: order.body.id)
paid = WaitUntilPaid(id: order.body.id)
expect { paid.body.total == 45.5 }
}
api/orders/wait.outry
WaitUntilPaid: GET /v1/orders/{id} {
poll body.status == "paid" every 1s for 10s
}

Каждый шаг — запрос из другого файла, вызванный как функция; Login() внутри них выполняется один раз за запуск и переиспользуется. Обычно это живёт в коллекции Postman или в отдельных тестах — рядом со спецификацией и в расхождении с ней.

Расхождение: кто заметит, что код изменился

Заголовок раздела «Расхождение: кто заметит, что код изменился»

Спецификацию пишут руками или генерируют из аннотаций в комментариях. В обоих случаях ничто не мешает обработчику измениться, а спецификации остаться прежней: переименованное поле, новый обязательный параметр, ещё один middleware. Первыми об этом узнают клиенты.

Outry читает сам код на Go — роуты, структуру, в которую читается тело, query-параметры, заголовки, middleware, тип ответа — без аннотаций. Каждый запрос .outry сравнивается со своим обработчиком, и CI падает на конкретной строке:

Окно терминала
$ outry check
api/v1/users/post.outry:17:8: error: required field `full_name` (string) is missing from body ← internal/api/users.go:13

Подробнее — в Импорте роутов из Go.

OpenAPI Outry
Назначение Описать контракт Запустить и проверить API
Отправляет запросы Нет (только «Try it out» в Swagger UI) Да: CLI, приложение, редакторы, CI
Проверяет значения и коды ответа Нет expect { … }
Сценарии из нескольких запросов Нет Запросы вызывают друг друга, flow, poll
Окружения и секреты Только servers env.toml, системное хранилище паролей, OUTRY_* в CI
Совпадает с кодом на Go Если кто-то обновил outry check сравнивает с обработчиками
Генерация клиентов, Swagger UI Да Нет

Outry не заменяет спецификацию — оставьте её для публичного контракта, документации и генерации клиентов. Outry нужен для того, чего спецификация не умеет: отправлять запросы, собирать из них сценарии и ловить расхождения с кодом в CI.

Если для тел ответов есть JSON Schema, Outry проверяет ответы по ним напрямую:

shape Order = schema("./schemas/order.json")

Импорт из OpenAPI в файлы .outry запланирован.