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

Генератор кода 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.yamlapp/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, пока не реализуете
DTOapp/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 из спеки добавляются в тот же файл, существующие методы с тем же именем не перезаписываются.
  • Методы на DTOAuthorize, 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() — см. автотесты.

Типичный рабочий процесс

  1. Изменили контракт в resources/openapi/ (paths, schemas, x-lg-handler).
  2. Запустили ensi-gog server, проверили diff (новые методы, обновлённые DTO).
  3. Реализовали или поправили action и тело хэндлера; при необходимости обновили Rules().
  4. Дописали component-тесты поверх заготовок TestStatus_*.
  5. Если сервисом пользуются другие команды — ensi-gog client, commit клиентского репозитория, bump зависимости у потребителей.

Спека остаётся единым источником правды: документация (/docs/oas), серверный код и клиенты расходятся из одного index.yaml.