API methods#
Операция представляет собой класс: его поля описывают запрос, а __http__ задаёт метод, путь и всё остальное, что
операция объявляет. Роутер (SyncApi или AsyncApi) публикует операцию через op(...), и
публичной сигнатурой SDK становится конструктор класса.
Операция как класс#
from dataclasses import dataclass
from pydantic import BaseModel
from eazy_sdk import UNSET, AsyncApi, Http, HttpOperation, Omittable, Path, Query, op
class User(BaseModel):
id: int
@dataclass(frozen=True, slots=True, kw_only=True)
class GetUser(HttpOperation[User]):
__http__ = Http.get("/users/{user_id}")
user_id: Path[int]
include: Query[Omittable[str]] = UNSET
page: Query[int] = 1
class UsersApi(AsyncApi):
get_user = op(GetUser)
HttpOperation[User] называет тип результата. Класс может быть dataclass, Pydantic-моделью или
msgspec-структурой; требование одно: он frozen, потому что запрос является значением. Значения по
умолчанию задаются обычными средствами Python, включая default_factory; поле, которое иногда не нужно
отправлять вовсе, объявляется Omittable[T] = UNSET.
HTTP-параметры объявляйте keyword-only (kw_only=True): у них нет осмысленного порядка, а
добавление поля не должно менять смысл позиционного вызова.
Декоратор как короткая форма#
@api.get(...) синтезирует ровно такой же frozen-класс. Он доступен как
UsersApi.get_user.Operation. Отличия двух форм и выбор между ними описаны на странице
Объявление операции.
Ответ операции#
Тип результата и есть успешный случай; success= нужен, только когда случаев несколько или у
ответа особый статус. Ошибки объявляются мэппингом:
from dataclasses import dataclass
from pydantic import BaseModel
from eazy_sdk import AsyncApi, Http, HttpOperation, Path, op
from eazy_sdk.response import ApiError
class NotFound(BaseModel):
detail: str
class UserNotFound(ApiError[NotFound]):
pass
@dataclass(frozen=True, slots=True, kw_only=True)
class GetUser(HttpOperation[User]):
__http__ = Http.get("/users/{user_id}", errors={404: UserNotFound})
user_id: Path[int]
class UsersApi(AsyncApi):
errors = ({429: RateLimited},)
get_user = op(GetUser)
Ошибки операции складываются с errors роутера; inherit_errors=False отключает наследование.
При совпадении статуса выигрывает случай, объявленный ближе к операции. Подробности приведены в
Ошибки API.
Namespace api и Http#
Одни и те же глаголы в двух формах:
Http.get,Http.post,Http.put,Http.patch,Http.delete,Http.head,Http.options,Http.trace: для класса;api.get,api.post, …: для декоратора.
Для другого валидного HTTP token есть общий вход: Http.request("PROPFIND", "/resources") и
api.request("PROPFIND", "/resources"). Невалидный token отклоняется при создании спецификации.
Параметры Http.* и api.*#
Параметры перечислены в порядке _HttpOptions: от часто используемых к редким. path является
единственным позиционным параметром.
Что возвращается
Параметр |
Назначение |
Default |
|---|---|---|
|
URL path с |
обязательный |
|
Успешные случаи, когда их больше одного или статус не |
тип результата |
|
Типизированные ошибки: |
|
Как выглядит запрос
Параметр |
Назначение |
Default |
|---|---|---|
|
|
нет |
|
|
наследуется |
Что сервис требует от запроса (всё наследуется по MRO роутера)
Параметр |
Назначение |
Default |
|---|---|---|
|
Схема или политика авторизации; |
наследуется |
|
Правила подписи; |
наследуется |
|
Профиль шифрования payload; |
наследуется |
|
Обязательные protection-требования |
|
|
Зависимости запроса ( |
|
|
Готовые значения в место запроса ( |
|
Редко нужное
Параметр |
Назначение |
Default |
|---|---|---|
|
Случай «на всё остальное», ключ |
нет |
|
Добавлять ли |
|
|
Явное разрешение на повтор для неидемпотентного метода |
выводится из метода |
|
Вернуть |
|
Имена
Параметр |
Назначение |
Default |
|---|---|---|
|
Стабильная identity операции в диагностике и схемах |
имя класса или метода |
|
Метаданные для генераторов и группировки |
|
Если success= или errors= не объявлены#
success= не нужен, когда успешный ответ один: тип из HttpOperation[T] уже образует случай
«200, семья по модели, модель T». Явный success= нужен для другого статуса ({201: Order}),
для нескольких успешных форм или для представления с медиа-типом и условием. Если не объявлено ни
HttpOperation[T], ни success=, операция отвергается при объявлении:
PlanError: operation class NoType declares neither HttpOperation[T] nor success=
errors= не обязателен вовсе. Без него у операции просто нет собственных типизированных ошибок:
остаются унаследованные от роутера и от ClientConfig(errors=…). Ответ, не подошедший ни под один
случай. Это не «тихий успех» и не None:
UnexpectedResponseError: GetUser received an undocumented response: 404 application/json
То есть недокументированный ответ всегда виден как ошибка контракта. Объявляйте errors= тогда,
когда хотите отличать 404 от 409 типом исключения, а не когда хотите «чтобы не падало».
Роутер объявляет base_url, security, signing, crypto, wire, errors, unwrap,
protocol, signed и allow обычными атрибутами класса; они наследуются по MRO, поэтому общий
для нескольких роутеров сервис описывается обычным базовым классом (см. Мультисервисный SDK).
fallback, inherit_errors и raw_response#
fallback задаёт один случай для всего, что не совпало ни с успешным ответом, ни с объявленной ошибкой. Он
существует отдельно от errors намеренно: по специфичности он слабее любого статуса и диапазона,
и стой он рядом с ними в одном мэппинге, он совпал бы с тем же телом, что и случай с кодом. Поэтому
DEFAULT в errors= отвергается при разборе объявления до первого запроса:
PlanError: errors= in 'getUser' cannot use DEFAULT; declare fallback= instead
Пишите fallback для сервиса, который отвечает одной и той же моделью проблемы на всё подряд:
fallback=Problem превращает UnexpectedResponseError в обычную ApiError[Problem].
inherit_errors=False отбрасывает errors роутера для одной операции. Нужен там, где сервисный
случай неверен именно здесь: например, роутер объявил {404: OrderNotFound}, а у этой операции
404 означает «ресурс ещё не создан» и должен разбираться другой моделью. Значение True по умолчанию
ошибки складываются, и при совпадении статуса выигрывает случай, объявленный ближе к операции.
raw_response=True отключает выбор случая: вызов возвращает NormalizedResponse со статусом,
заголовками и телом, и ни одна модель не строится. Это выход для ответов, форму которых SDK не
описывает, например проксирование, скачивание файла или разбор в приложении. Это не способ «посмотреть
статус»: для статуса и заголовков рядом с разобранной моделью есть with_response(...), которое
проходит те же случаи и возвращает ResponseEnvelope.
Запрос как значение#
Связанная операция умеет строить, изменять и отправлять запрос. См. Запрос как значение:
request = users.get_user.request(user_id=42)
await users.get_user.send(users.get_user.evolve(request, page=2))
Подготовка без отправки#
Bound operation поддерживает ту же sync/async форму через .prepare(...):
prepared = await users.get_user.prepare(
user_id=42,
options=PrepareOptions(resolve_managed=False),
)
prepare выполняет обычные bind/default/project/encode/sign стадии и останавливается на общей
границе перед handler send. Pure mode не вызывает providers и возвращает
PreparationIncompleteError, если нужны managed values. Full mode (resolve_managed=True)
использует test providers, но сеть всё равно не вызывается. PreparedCall редактирует secret
headers, cookies, query и private body.