Comments 16
использовал в практике https://github.com/oapi-codegen/oapi-codegen/ и как раз четко под задачу.
Комплексно слишком он ограниченный и множество мест руками надо делать. Но если вам надо openapi-first что бы документация была 100% валидной то тогда он ваш выбор. Плюс в нашей задаче была повышенная безопасность и потому сверху без проблем допились свои фильтры, что позволило собирать бинарник для клиента где все вшито и мы были уверены что клиент никак его не запустит и не подключит, что бы было небезопасно в первую очередь для клиента.
из минусов те самые ограничения - только openapi и то не прям весь, валидаторы один хрен самим писать и тесты к ним нормально, нет вебсокетов (но это известная проблема openapi в первую очередь) и сложности если апи отдает что то более чем json/xml
Интересный опыт использования этой библиотеки, круто что ты им поделился!
У нас на проекте была самописная библиотека, в которой, в том числе, есть сервер и клиент. Поэтому искали генератор, которым можно генерировать, используя свой внутренний код. Отсюда такие познания в шаблонах и много примеров с ними)))
Насчёт одного из минусов: для валидаций можно прописывать прямо в OpenAPI спецификации тэг "x-oapi-codegen-extra-tags", в котором прописывать тэг validate, который генерируется и затем обрабатывается пакетом go-playground/validator. Пример: [https://blog.commitsmart.com/go-oapi-codegen-request-validation-285398b37dc8]. Просто если бы я рассказывал об этом в статье, она бы превратилась из лонгрида в лонг-лонгрид, показать на примере ozzo-validation с более понятной логикой мне показалось более наглядным и простым)
мы иначе валидацию реализовали)
так как все есть объект и openapi является и конструктор в том числе как protobuf при генерации, мы просто привязались к этим структурам
то есть в самой документации мы Х-поле с именем функции -> функция работает со структурой которая генерируется из openapi -> в самом генераторе сделали подстановку валидатора как эту функцию
то есть мы сохранили прозрачность кода и при этом никаких рефлексий, все пробегает при генерации.
нет вебсокетов
Тоже приходилось для описания асинхронных взаимодействий, отдельно поддерживать спецификацию на AsyncAPI.
Ну а если вдруг захотите внедрить это в легаси, моя рекомендация — не пытайтесь переписать все сразу
Я бы сперва в обратную сторону сгенерил OpenAPI-спецификацию из легаси (многие фреймворки умеют это из коробки), а потом доработал и на основе неё реализовал бы уже spec-driven подход.
Спасибо! Разделяю этот подход, а для тех, кто страдает и пишет опенапи руками - рекомендую TypeSpec. Из него же можно и клиенты накодогенерить
Согласен с вами, многие сейчас уверовали в ИИ, когда он по сути - бомба замедленного действия и не дает гарантий повторяемости, его вечно нужно перепроверять, да порой несет что попало, это всего-лишь очередной инструмент не более.
Когда кодогенерация гарантированно решает многие проблемы. Раньше в пыхе использовал openapi-codegen и все сразу же заиграло новыми красками, щас в go также перешел на openapi-codegen и просто кайф. Никакой ИИ не сравнится с полученым результатом. На выходе с кодогенерацией мы имеем гарантии, строгую типизацию, валидацию, хендлеры, тесты все из коробки.
Но для многих до сих пор это почемуто кажется сложным и нереальным, это так, пока люди не попробуют чутка не подстроят свою инфраструктуру под кодеген.
При повторной кодогенерации все ручные изменения стерутся ?
опасненько так жить если всё стерётся...
Если нужно какие-то методы, например, дописать - это можно сделать в отдельном файле. А если хочется сгенерированный код переписывать, то это плохая идея (даже IDE об этом предупредит), ибо при повторной генерации изменения пропадут. Лучше изменить шаблон, тогда результат генерации будет воспроизводимым. Этого мы, в том числе, хотим, вводя spec-first подход.
у вас указано, что есть еще protobuf для grpc. Как вы в таком случае синхронизируете контракт между openapi и protobuf и гарантируете совместимость?
я такую же проблему решал, но с другой стороны. нужно было с grpc переходить на http, решил сохранить proto файлы и поверх них написать генератор.
Кажется такой подход чуть лучше работает:
общий контракт для gprc и http
правила валидации можно в том же proto прописать
openapi можно из proto генерировать
Моя команда генерации так выгядит:
--go_out=paths=source_relative:. - структуры го
--go-grpc_out=paths=source_relative:. - grpc обертка
--validate_out=lang=go,paths=source_relative:. - валидатор (envoyproxy/protoc-gen-validate)
--openapi_out=output_mode=source_relative:. openapi(google/gnostic)
--httpgo_out=paths=source_relative:. http server+client (моё решение https://github.com/MUlt1mate/protoc-gen-httpgo)
Со случаями синхронизации openapi и protobuf не сталкивался, спасибо Вам, что поделились таким интересным опытом! А вот переводить легаси-сервис с http на grpc приходилось, но синхронизация там не требовалась: сервис был полностью доделан. Просто на серваке сначала сделал grpc+http, потом перевел сервисы-клиенты на grpc, затем выпилил http-сервер. Для «чернового» перевода одного в другое сервисов и утилит нашел предостаточно, это радует
Protobuf я упомянул лишь по той причине, что показывал, как у нас устроен репозиторий контрактов, в котором мы храним и генерируем не только openapi, но ещё и proto с cloudevents. Ну и потому, что он изначально с рассчётом на Documentation-Driven разработку создавался
Так ваш протобаф гошный может сразу и в хттп и в грпс.
Я Думал все гоферы с молоком матери начинаюь сначала протобаф описывать, а потом проток генерейть и готов хендлер какой хошь. Проток мок и вот тебе заглушки товарищь фронтендер.
Апи фесту уже 1000 лет в обед.
А SDD - спекулянтус дривнус девелопментус - это шкодерам жизнь проделвает. Написал спеку для ллм. А она тебе пофиг какой код, главное чтоб лампочками моргал)
Компилятор первым сообщает, что именно надо поправить в реализации.
Причём тут компилятор? У вас там panic() — это runtime.
То есть чтобы всё это отловить - всё api должно быть покрыто тестами. А их как вы автогенерите?
Тут имелось в виду: когда мы изменили сигнатуру в спецификации и перегенерировали код, IDE, а затем и компилятор Go выдадут ошибку (несоответствия нашей реализации сервера сгенерированному интерфейсу сервера) на строке с вызовом функции RegisterHandlers.
Компилятор тут при том, что один из этапов компиляции — Type-checking and AST transformations (cmd/compile/internal/gc) — тут происходит магия по авто-типизации, проверка интерфейсов, определение мертвого кода и escape-анализ
Documentation-Driven Development: как мы генерируем Go-код из OpenAPI-спецификаций