Files
admin/.wiki/concepts/openapi-generator-3-1-spec-gotchas.md

67 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: openapi-generator + OpenAPI 3.1 — gotchas генерации клиентов
type: concept
tags: [openapi, openapi-generator, codegen, python, sched, ff, api-contract]
related: [[sched-fnf-r8-verification-stand]]
updated: 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-транспорт + ручная валидация:
```python
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"])
```
Важно: в raw-вызове `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-generator` 7.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).