4.4 KiB
title, type, tags, related, updated
| title | type | tags | related | updated | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| openapi-generator + OpenAPI 3.1 — gotchas генерации клиентов | concept |
|
|
2026-08-18 |
openapi-generator + OpenAPI 3.1 — gotchas
Проверено вживую на F&F-раунде sched «клиент на любом ЯП одной командой»
(2026-08-18): Python-клиент из @sched/admin-api@0.1.2 спеки (3.1.0, 15 путей)
через openapi-generator 7.24.0 (-g python, pydantic v2). Спеку НЕ чинили
(их импл, TDD) — обходили. Находки F-1..F-5 полные — в
sched/.agents/inbox/2026-08-18T19-15-43Z-admin-fnf-openapi-client-report.md;
здесь — переиспользуемая суть.
Гоча 1 — ответы {} в спеке ломают десериализацию (главная)
Если операция объявляет ответ {} (пустая схема), а сервер реально отдаёт
объект ({task: …}, {run: …}, {schedule: …}, {logChunk, logTotalLength}),
сгенерированный клиент валится на pydantic ValidationError — он пытается
десериализовать wrapped-тело в объявленную модель, а требуемых полей на верхнем
уровне нет (все None).
- Симптом:
ValidationErrorна 2xx с полямиinput_value=None. - Фикс (у имплементера спеки): объявлять реальные схемы ответов
(
{task: TaskRecord}и т.п.). Без этого ни один генератор не даёт рабочий клиент. - Обход (у потребителя): raw-транспорт + ручная валидация:
Важно: в raw-вызове
resp = client.call_api("POST", client.configuration.host + "/tasks", body=body) data = json.loads(resp.read()) # resp.data может быть None до read() rec = TaskRecord.model_validate(data["task"])call_apiНЕ делает snake→camel конвертацию запроса — сериализуй черезmodel_dump(exclude_none=True, by_alias=True).
Гоча 2 — const: true компилится в строковый enum
"ok": { "type": "boolean", "const": true } → openapi-generator генерит
enum ('true') со СТРОКОВЫМ значением → настоящий true (boolean) не проходит
валидацию. Убирать const из схемы или не использовать его на булевых полях.
Гоча 3 — oneOf + discriminator не десериализуется
Schedule (kind: cron|interval|once, oneOf + discriminator) в сгенерированной
модели приходит с actual_instance=None — правило (cron) недостижимо через
модель, только через raw JSON: raw["schedule"]["schedule"]["cron"].
Касается любого oneOf+discriminator на этой версии генератора (3.1-ветка).
Окружение (Windows)
openapi-generator7.x требует Java 11+: системная Java 8 падает на class-load; ставьJAVA_HOMEна новый JDK (Temurin 25 работает). Запуск:npx --yes @openapitools/openapi-generator-cli generate -i spec.json -g python -o out --additional-properties=packageName=...- Windows excluded port ranges (Hyper-V/WinNAT) резервируют порты (напр. 8090/8091):
EACCESна listen. Проверка:netsh interface ipv4 show excludedportrange protocol=tcp. - Консоль cp1251: не печатай
→/«»в скриптах —'charmap' codec can't encode; ASCII-only.
Паттерн обхода (потребитель, клиент не трогаем)
Тонкий raw-слой рядом с сгенерированным пакетом (в примере — raw_ops.py):
только операции, где спека {}, через call_api; остальное — через типизированный
клиент. Сгенерированный пакет остаётся перегенерируемым.
Пример целиком: sched/examples/admin-client-python/ (24/24 сьют, 19/19 smoke).