Skip to content

API-контракты: полное покрытие OpenAPI + coverage-тест, MCP err.body/дрейф/новые инструменты#86

Merged
friench merged 2 commits into
mainfrom
api/contracts
Jul 8, 2026
Merged

API-контракты: полное покрытие OpenAPI + coverage-тест, MCP err.body/дрейф/новые инструменты#86
friench merged 2 commits into
mainfrom
api/contracts

Conversation

@friench

@friench friench commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Четвёртый заход — API-контракты. Два коммита.

#78 — OpenAPI: полное покрытие + защита от дрейфа

Манифест документировал ~35 операций из ~90 смонтированных — целые ресурсы (sync, quarantine, users, feature-flags, backups, settings, me/*) и почти все item-level CRUD отсутствовали, так что клиент, сгенерированный из /openapi.json, получал половину API. Добил каждый смонтированный admin-роут (request-body подтянуты из реальных zod-валидаторов, где они есть).

Добавлен tests/integration/openapi-coverage: интроспектит смонтированный Express-роутер и проверяет, что каждый роут либо в спеке, либо в отревьюенном allow-list (autoconfig, OIDC-редиректы, /metrics); плюс обратная проверка (нет пути в спеке, которого нет в роутере). Манифест больше не может молча разойтись — новый router.get(...) роняет CI, пока не задокументирован.

Заодно фикс версии в Docker: node dist/index.js не выставляет npm_package_version, поэтому /openapi.json отдавал 0.1.0 — новый хелпер appVersion() читает package.json как fallback.

#79 — MCP: детали ошибок, дрейф схем, недостающие инструменты

  • fail() теперь дописывает err.body — бэкендовый 400 с {error, issues:[{path,message}]} доходит до модели, и она может само-скорректироваться вместо голого «Validation error».
  • ok() отдаёт compact-JSON (был pretty-print) — большие списки вроде list_quarantine по всем ящикам не топят контекст.
  • Дрейф схем: webhook events — теперь реальный enum (вкл. send.bounced) на create+update вместо z.array(z.string()); update_smtp_account получил недостающие active/domainId/userEnvVar/passwordEnvVar (теперь умеет деактивировать аккаунт, а не только удалять); dns_check получил ?refresh.
  • Новые инструменты (69 → 82): generate_temp_alias, update_alias, regenerate_access_rules, update_fetchmail (пауза), delete_migration, bulk_quarantine, get_quarantine_message, set_user_password, reset_feature_flag, а также item-reads get_webhook/get_smtp_account/get_settings.

Проверки

Backend: typecheck · lint · format:check · 562 теста ✓ (+2 coverage). MCP: typecheck · build ✓; счётчик инструментов в README и коде совпадает (82).

Не вошло из #79: бэкендовые limit-параметры для bounces/suppressions/quarantine — это по сути пагинация (отдельная архитектурная тема), не MCP-фикс. Оставляю за скобками.

Closes #78
Closes #79

🤖 Generated with Claude Code

friench added 2 commits July 8, 2026 00:16
The manifest documented ~35 operations of ~90 mounted — entire resources
(sync, quarantine, users, feature-flags, backups, settings, me/*) and nearly all
item-level CRUD were missing, so a client generated from /openapi.json got half
the API. Backfilled every mounted admin route (request bodies wired from the
real zod validators where they exist).

Adds tests/integration/openapi-coverage — introspects the mounted Express router
and asserts every route is either in the spec or a reviewed allow-list
(autoconfig, OIDC redirects, /metrics), plus the inverse (no spec path that
isn't mounted). The manifest can no longer silently drift: a new router.get(...)
fails CI until it's documented.

Also fixes the version served in Docker: `node dist/index.js` left
npm_package_version unset so /openapi.json advertised 0.1.0 — a new appVersion()
helper falls back to reading package.json.

Closes #78
…ools

- fail() now appends the MailApiError body, so a backend 400's
  {error, issues:[{path,message}]} reaches the model — it can self-correct
  instead of seeing a bare "Validation error".
- ok() emits compact JSON (was pretty-printed), so large lists like
  list_quarantine across every mailbox don't flood the context window.
- Schema drift fixes: webhook `events` is now the real enum (incl. send.bounced)
  on create+update instead of z.array(z.string()); update_smtp_account gains the
  active/domainId/userEnvVar/passwordEnvVar fields it was missing (so it can
  deactivate an account, not only delete it); dns_check gains ?refresh.
- New tools (69 → 82): generate_temp_alias, update_alias, regenerate_access_rules,
  update_fetchmail (pause), delete_migration, bulk_quarantine,
  get_quarantine_message, set_user_password, reset_feature_flag, and the
  get_webhook / get_smtp_account / get_settings item reads.

Refs #79
@friench friench merged commit 89b45ca into main Jul 8, 2026
1 check passed
@friench friench deleted the api/contracts branch July 8, 2026 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant