Генератор кода ensi-gog
В Go-сервисах Ensi контракт HTTP API задаётся OpenAPI-спецификацией (как и в PHP-сервисах на Laravel). Чтобы не поддерживать роуты, DTO и заготовки хэндлеров вручную, используется CLI ensi-gog — генератор из репозитория goravel-openapi-generator.
Генератор решает две задачи:
ensi-gog server— серверный код внутри сервиса, который описывает API (роуты, типы, хэндлеры, тесты);ensi-gog client— отдельный Go-модуль HTTP-клиента к этому или другому сервису, чтобы потребители вызывали API типизированно.
Бизнес-логику (actions), модели БД, middleware «по смыслу», Kafka и прочую инфраструктуру генератор не пишет — только каркас вокруг контракта API.
Установка
Установите бинарник в GOPATH/bin (путь должен быть в PATH):
go install gitlab.com/greensight/ensi/packages/goravel-openapi-generator/cmd/ensi-gog@latest
Проверка версии:
ensi-gog --version
Файл настроек .ensi-openapi-gen.yaml
В корне репозитория сервиса, чья спека является источником правды, лежит файл .ensi-openapi-gen.yaml. Все пути в нём считаются относительно этого файла.
Минимально для генерации сервера нужен только путь к entrypoint спеки:
spec: resources/openapi/index.yaml
Для генерации клиента в том же файле добавляют секцию client: (см. ниже).
Перед чтением конфигурации генератор подгружает .env из той же директории, что и YAML. В значениях можно использовать подстановки ${VAR} и ${VAR:-default} — например, каталог вывода клиента через ${OPENAPI_CLIENT_OUTPUT_DIR}.
Если во внешней или общей спеке встречаются одинаковые имена схем в разных файлах, их можно развести через component_aliases (ключ — путь фрагмента относительно корня OpenAPI, значение — уникальное имя типа). Это работает и для server, и для client.
Где лежит OpenAPI в Go-сервисе
Точка входа — resources/openapi/index.yaml.
Модули API обычно разнесены по подпапкам (offers/, auth/, categories/), рядом лежат *_paths.yaml и schemas/.
При генерации серверного кода, эти папки становятся модулями приложения. Примеры:
auth/paths.yaml→ код вapp/modules/auth/...;cms/banners/paths.yaml→app/modules/cms/banners/...(вложенные каталоги сохраняются);
Общие параметры и ошибки определяются в корневых файлах вроде common_parameters.yaml.
Имя HTTP-контроллера и метод хэндлера задаются в спеке расширением x-lg-handler на операции:
OffersSearch:
post:
operationId: searchOffers
x-lg-handler:
handler: OffersController
method: search
Для обратной совместимости со спеками в сервисах на php,
x-lg-handlerможет принимать строку, напримерApp\Http\ApiV1\Modules\Seo\Controllers\SeoTemplateProductsController@get
Из этого генератор строит структуру OffersController, метод Search и файл тестов search_test.go. Дополнительные middleware на группу роутов можно описывать через x-lg-middleware (как принято в вашей спеке Ensi).
Подробнее про общие правила дизайна API — API Design Guide.
Команда ensi-gog server
Запуск из корня сервиса (где лежит .ensi-openapi-gen.yaml):
ensi-gog server
Или с явным путём к настройкам:
ensi-gog server ./.ensi-openapi-gen.yaml
Генератор собирает (bundle) спецификацию во временный файл, разбирает операции и создаёт или дополняет артефакты:
| Что появляется | Куда | Зачем |
|---|---|---|
| Роуты | routes/api_gen.go | Регистрация /api/v1/... и привязка к хэндлерам |
| Хэндлеры | app/modules/{module}/handlers/ | Точка входа HTTP; stub отвечает 501, пока не реализуете |
| DTO | app/modules/{module}/dto/ | Тела запросов и типы data в ответах |
| Enum'ы | app/enums/*.gen.go | Перечисления из спеки |
| Фабрики запросов | app/modules/{module}/factories/ | Заготовки для тел POST/PATCH в тестах |
| Component-тесты | app/modules/{module}/tests/{handler}/ | Заготовки TestStatus_* по кодам ответа из спеки |
| Хелперы | app/common/http/validate.go, map.go | Валидация FormRequest и маппинг в DTO (создаются один раз) |
Файл routes/api.go создаётся один раз и вызывает сгенерированный RegisterApiRoutes(). Его можно расширять, но вызов регистрации API лучше оставить.
Для request-DTO с телом запроса генератор добавляет методы Authorize и Rules (как FormRequest в Goravel). Для response-data — метод From(src any) через общий маппер. Обёртки вроде «ответ с полем errors» в Go-код не генерируются: в типах остаётся только полезная нагрузка (data), а формат ответа задаётся общими хелперами HTTP-слоя.
Заготовка метода хэндлера обычно валидирует request (если есть схема) и возвращает 501 Not Implemented. Реализацию (вызов action, маппинг, ошибки) пишете вы.
Что можно править после генерации
Повторный ensi-gog server не затирает вашу работу там, где генератор рассчитан на доработку:
- Тела методов хэндлеров — свободно меняете; новые action из спеки добавляются в тот же файл, существующие методы с тем же именем не перезаписываются.
- Методы на DTO —
Authorize,Rules,Fromи любые свои; генератор обновляет только объявления полей struct из спеки. - Свои типы в том же
dto/*.go, которых нет в OpenAPI — остаются. - Фабрики запросов — можно менять
Definition()и добавлять свои методыWith*. - Component-тесты — после первого создания файла генератор их не трогает; дополняете сценарии сами (см. автотесты на Go).
validate.go/map.go— не перезаписываются даже с--force.
Что нельзя править вручную (или правки пропадут)
routes/api_gen.go— файл с пометкойDO NOT EDIT; пути, методы, middleware меняются только в OpenAPI.- Поля сгенерированных struct DTO и их
json/formтеги — при следующей генерации type-блок подменится из спеки. Новые поля добавляйте в YAML-схемы. - Файлы
*.gen.goс enum'ами — перед генерацией пересоздаются; значения только в спеке (x-lg-enum-classи т.д.). - Имена модулей, контроллеров, action — задаются путём в OpenAPI и
x-lg-handler; переименование только в Go без спеки приведёт к дублям.
Важный нюанс: если вы уже написали Rules() вручную, а в спеке изменился набор полей request, генератор обновит struct, но не перепишет ваш Rules(). После изменения схемы правила валидации нужно проверить и поправить самостоятельно.
Генератор не создаёт actions, модели БД, policies и query-контракты для search — это остаётся за разработчиком (см. структуру сервиса).
Команда ensi-gog client
Секция client в .ensi-openapi-gen.yaml описывает, куда положить сгенерированный модуль-клиент и как он будет называться в экосистеме Ensi.
Пример:
spec: resources/openapi/index.yaml
client:
name: catalog-cache
output: ${OPENAPI_CLIENT_OUTPUT_DIR}-go
module: gitlab.com/greensight/ensi/catalog/clients/catalog-cache-client-go
repository: gitlab.com/greensight/ensi/catalog/clients/catalog-cache-client-go
base_url_env: CATALOG_CATALOG_CACHE_GO_SERVICE_HOST
http_runtime: gitlab.com/greensight/ensi/packages/goravel-ensi-http-client
go_mod: |
go 1.25.0
require (
gitlab.com/greensight/ensi/packages/goravel-ensi-http-client v0.0.0-20260806150005-69f7fd80d898
github.com/goravel/framework v1.18.0
github.com/oapi-codegen/runtime v1.6.0
)
Кратко по полям:
name— короткий идентификатор (влияет на название файла провайдера и ключ конфигаensi_clients.{snake_name});output— каталог модуля клиента (часто через переменную из.env);module— путьgo.modсгенерированного клиента;repository— Git-репозиторий клиента (для документации);base_url_env— имя переменной окружения с базовым URL сервиса чтобы генератор прописал его в конфиг клиента;http_runtime— имя пакета goravel-ensi-http-client, чтобы генератор прописал его во все импорты клиента;go_mod— телоgo.modклиента без строкиmodule(версии зависимостей).
Запуск генерации клиента — из любого места, указав настройки сервиса-источника спеки:
ensi-gog client /path/to/service/.ensi-openapi-gen.yaml
Клиент генерируется целиком из OpenAPI: типы, методы API, фабрики для тестов, fake-обёртки для component-тестов потребителей. После изменения спеки перегенерируйте клиент и обновите зависимость в сервисах-потребителях.
Подключение клиента в Goravel-сервисе
Сгенерированный клиент оформлен как Goravel package. Его нужно устанавливать через artisan:
./artisan package:install gitlab.com/greensight/ensi/cms/clients/cms-client-go@latest
После установки в потребителе появляются:
app/providers/clients/{name}_provider.go— регистрация клиента в DI (можно добавить middleware логирования/метрик);config/ensi_clients/{name}.go— чтение URL из env (base_url_envиз настроек генерации).
Провайдер подключается в bootstrap/providers.go. Конфиг подхватывается через init-механизм Goravel. Оба файла можно редактировать под проект.
В тестах потребителя stub ответов апстрима делается через ClientFake() и Bind() — см. автотесты.
Типичный рабочий процесс
- Изменили контракт в
resources/openapi/(paths, schemas,x-lg-handler). - Запустили
ensi-gog server, проверили diff (новые методы, обновлённые DTO). - Реализовали или поправили action и тело хэндлера; при необходимости обновили
Rules(). - Дописали component-тесты поверх заготовок
TestStatus_*. - Если сервисом пользуются другие команды —
ensi-gog client, commit клиентского репозитория, bump зависимости у потребителей.
Спека остаётся единым источником правды: документация (/docs/oas), серверный код и клиенты расходятся из одного index.yaml.