wiki: ingest round-2 outcome — openapi-generator gotchas + sched-admin-client-openapi updated (all F-1..F-5 closed, raw_ops deleted)

This commit is contained in:
2026-08-18 22:49:08 +03:00
parent b019983906
commit 34e6e2c7ab
4 changed files with 77 additions and 19 deletions

View File

@@ -8,12 +8,38 @@ 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`;
здесь — переиспользуемая суть.
Проверено вживую на F&F-раундах sched «клиент на любом ЯП одной командой»
(2026-08-18): Python-клиент из спеки `@sched/admin-api` через
`openapi-generator` **7.24.0** (`-g python`, pydantic v2). Спеку НЕ чинили
обходили (round 1, 0.1.2). Round 2 (0.2.0): все гочи 14 **закрыты фиксами
спеки/сервера**, raw-обход удалён — см. «Статус после round 2» ниже.
## Статус после round 2 (0.2.0 / daemon 0.3.5)
Все 5 находок round 1 подтверждены закрытыми (ретест 2026-08-18, отчёт
`sched/.agents/inbox/2026-08-18T22-55-00Z-admin-openapi-fixes-round2-retest.md`):
- **Гоча 1 (wrapped-ответы) — FIXED (спека).** Мутации теперь объявляют
`{task}/{run}/{schedule}` (response-компоненты TaskMutation/RunMutation/
ScheduleMutation), `GET /runs/{id}?logFromOffset` — RunLogChunk (allOf
RunRecord + logChunk/logTotalLength). Генератор выдаёт **InlineObjectN**-
модели (поля task/run/schedule/ok), десериализация нативная. `raw_ops.py`
из примера **удалён**.
- **Гоча 2 (const:true) — FIXED.** `const` убран из спеки; `Health200Response.ok`
= обычный `StrictBool`.
- **Гоча 3 (oneOf+discriminator) — FIXED.** `Schedule` стал плоской схемой
(`kind` + опциональные `cron/timezone/ms/at`); `rec.schedule.cron` читается
с типизированной модели. Normalization живьём: `once``{kind:once, at:ISO}`,
`interval``{kind:interval, ms}`.
- **F-1 (health auth) — FIXED (сервер).** `/health` всегда открыт, auth —
после health-роута; прочие роуты с ключом 401.
- **F-5 (dedupKey fail-fast) — FIXED (сервер).** POST/PATCH /schedules с
неизвестным top-level полем → 400 с именем поля.
- Итог ретеста: **11/11 ранее-сырых операций идут через типизированный клиент,
сьют 24/24, smoke 19/19** (коммит sched `d658ff0`).
Гочи 13 ниже — описание сломанного состояния round 1 (0.1.2) + паттерн обхода
(потребительский escape-hatch, если спека где-то ещё сырая).
## Гоча 1 — ответы `{}` в спеке ломают десериализацию (главная)
@@ -47,20 +73,35 @@ updated: 2026-08-18
модели приходит с `actual_instance=None` — правило (`cron`) недостижимо через
модель, только через raw JSON: `raw["schedule"]["schedule"]["cron"]`.
Касается любого oneOf+discriminator на этой версии генератора (3.1-ветка).
Фикс (round 2): плоская output-схема вместо oneOf — `kind` + опциональные
поля; per-kind валидация на сервере (parseScheduleEntry).
## Окружение (Windows)
- `openapi-generator` 7.x требует **Java 11+**: системная Java 8 падает на
class-load; ставь `JAVA_HOME` на новый JDK (Temurin 25 работает).
**Важно (round-2 уточнение):** CLI-обёртка (`@openapitools/openapi-generator-cli`)
спавнит `java` из **PATH**, не из `JAVA_HOME` — одного JAVA_HOME мало:
`$env:Path = "$env:JAVA_HOME\bin;" + $env:Path` до запуска, иначе
UnsupportedClassVersionError (class file 55.0 vs JRE 52.0 = Java 8 в PATH).
Запуск: `npx --yes @openapitools/openapi-generator-cli generate -i spec.json -g python -o out --additional-properties=packageName=...`
- Wrapped-ответы, объявленные **инлайн в `components.responses`**, дают имена
`InlineObject..InlineObjectN` и доступ `resp.schedule.schedule.cron` (двойной
`.schedule`). Работает; красивее — именованные схемы в `components.schemas`.
- `info.version` спеки НЕ бьётся вместе с версией пакета (в 0.1.2 и 0.2.0 —
`0.1.0`); версия живёт только в package.json / dist-tags.
- 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`):
Тонкий raw-слой рядом с сгенерированным пакетом (в примере был `raw_ops.py`):
только операции, где спека `{}`, через `call_api`; остальное — через типизированный
клиент. Сгенерированный пакет остаётся перегенерируемым.
Пример целиком: `sched/examples/admin-client-python/` (24/24 сьют, 19/19 smoke).
> **Статус:** после round 2 (0.2.0) обход не нужен — `raw_ops.py` удалён из
> примера. Паттерн остаётся escape-hatch'ем для сырых спек в других проектах.
Пример целиком: `sched/examples/admin-client-python/` (24/24 сьют, 19/19 smoke,
typed-only).