From 6c1072476b9fc436b47a863b3acc794a5267434c Mon Sep 17 00:00:00 2001 From: Manuel Polo Date: Mon, 25 May 2026 19:29:53 +0000 Subject: [PATCH 1/2] feat: regenerate client for auth token endpoint (0.95.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picks up OpenAPI spec 0.95.0: - New api.auth module with sync/async entrypoints against POST /auth/token - AuthTokenResponse, AuthError, AuthErrorCode, AuthTokenResponseTier, AuthTokenResponseTokenType models - apiKeyAuth (X-API-Key) security scheme Diff stat after regeneration is purely additive — the carry-forward schema changes (EquityPoint, ResultMap.equityCurve, etc.) had already landed on main. Test surface: - Add api.auth re-export at qtsurfer.api.client.api.auth. - Extend smoke tests to assert auth endpoint and AuthTokenResponse / AuthError models are reachable from the public surface. --- README.md | 1 + pyproject.toml | 2 +- .../client/_generated/api/auth/__init__.py | 1 + .../api/client/_generated/api/auth/auth.py | 174 ++++++++++++++++++ .../api/client/_generated/models/__init__.py | 10 + .../client/_generated/models/auth_error.py | 72 ++++++++ .../_generated/models/auth_error_code.py | 10 + .../_generated/models/auth_token_response.py | 101 ++++++++++ .../models/auth_token_response_tier.py | 11 ++ .../models/auth_token_response_token_type.py | 8 + src/qtsurfer/api/client/api/__init__.py | 4 +- src/qtsurfer/api/client/api/auth.py | 5 + tests/test_smoke.py | 6 + uv.lock | 2 +- 14 files changed, 403 insertions(+), 4 deletions(-) create mode 100644 src/qtsurfer/api/client/_generated/api/auth/__init__.py create mode 100644 src/qtsurfer/api/client/_generated/api/auth/auth.py create mode 100644 src/qtsurfer/api/client/_generated/models/auth_error.py create mode 100644 src/qtsurfer/api/client/_generated/models/auth_error_code.py create mode 100644 src/qtsurfer/api/client/_generated/models/auth_token_response.py create mode 100644 src/qtsurfer/api/client/_generated/models/auth_token_response_tier.py create mode 100644 src/qtsurfer/api/client/_generated/models/auth_token_response_token_type.py create mode 100644 src/qtsurfer/api/client/api/auth.py diff --git a/README.md b/README.md index 6e04341..7106441 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Each generated endpoint module exposes four entrypoints: | Module | Operation | Method · Path | | --- | --- | --- | +| `api.auth` | `auth` | `POST /auth/token` — exchange API key for a short-lived JWT | | `api.exchange` | `get_exchanges` | `GET /exchanges` | | `api.exchange` | `get_instruments` | `GET /exchange/{exchangeId}/instruments` | | `api.exchange` | `get_exchange_tickers_hour` | `GET /exchange/{exchangeId}/tickers/{base}/{quote}` | diff --git a/pyproject.toml b/pyproject.toml index a17bc64..7445856 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qtsurfer-api-client" -version = "0.94.0" +version = "0.95.0" description = "Auto-generated Python client for the QTSurfer API." readme = "README.md" license = "Apache-2.0" diff --git a/src/qtsurfer/api/client/_generated/api/auth/__init__.py b/src/qtsurfer/api/client/_generated/api/auth/__init__.py new file mode 100644 index 0000000..2d7c0b2 --- /dev/null +++ b/src/qtsurfer/api/client/_generated/api/auth/__init__.py @@ -0,0 +1 @@ +"""Contains endpoint functions for accessing the API""" diff --git a/src/qtsurfer/api/client/_generated/api/auth/auth.py b/src/qtsurfer/api/client/_generated/api/auth/auth.py new file mode 100644 index 0000000..c6c0399 --- /dev/null +++ b/src/qtsurfer/api/client/_generated/api/auth/auth.py @@ -0,0 +1,174 @@ +from http import HTTPStatus +from typing import Any, cast + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.auth_error import AuthError +from ...models.auth_token_response import AuthTokenResponse +from ...types import Response + + +def _get_kwargs() -> dict[str, Any]: + + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/auth/token", + } + + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Any | AuthError | AuthTokenResponse | None: + if response.status_code == 200: + response_200 = AuthTokenResponse.from_dict(response.json()) + + return response_200 + + if response.status_code == 401: + response_401 = AuthError.from_dict(response.json()) + + return response_401 + + if response.status_code == 429: + response_429 = cast(Any, None) + return response_429 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[Any | AuthError | AuthTokenResponse]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: AuthenticatedClient, +) -> Response[Any | AuthError | AuthTokenResponse]: + """Exchange API key for a short-lived JWT + + Exchanges a long-lived API key for a short-lived JWT used by every other + endpoint. This is the only endpoint that accepts an API key directly — + callers should obtain a JWT here, then send it as `Authorization: Bearer + ` to all other operations. + + The returned JWT carries the caller's subscription `tier` as a claim and + expires after `expires_in` seconds. Callers should refresh the token + before expiry (or on a `401` response) by calling this endpoint again. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any | AuthError | AuthTokenResponse] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient, +) -> Any | AuthError | AuthTokenResponse | None: + """Exchange API key for a short-lived JWT + + Exchanges a long-lived API key for a short-lived JWT used by every other + endpoint. This is the only endpoint that accepts an API key directly — + callers should obtain a JWT here, then send it as `Authorization: Bearer + ` to all other operations. + + The returned JWT carries the caller's subscription `tier` as a claim and + expires after `expires_in` seconds. Callers should refresh the token + before expiry (or on a `401` response) by calling this endpoint again. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Any | AuthError | AuthTokenResponse + """ + + return sync_detailed( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient, +) -> Response[Any | AuthError | AuthTokenResponse]: + """Exchange API key for a short-lived JWT + + Exchanges a long-lived API key for a short-lived JWT used by every other + endpoint. This is the only endpoint that accepts an API key directly — + callers should obtain a JWT here, then send it as `Authorization: Bearer + ` to all other operations. + + The returned JWT carries the caller's subscription `tier` as a claim and + expires after `expires_in` seconds. Callers should refresh the token + before expiry (or on a `401` response) by calling this endpoint again. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any | AuthError | AuthTokenResponse] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient, +) -> Any | AuthError | AuthTokenResponse | None: + """Exchange API key for a short-lived JWT + + Exchanges a long-lived API key for a short-lived JWT used by every other + endpoint. This is the only endpoint that accepts an API key directly — + callers should obtain a JWT here, then send it as `Authorization: Bearer + ` to all other operations. + + The returned JWT carries the caller's subscription `tier` as a claim and + expires after `expires_in` seconds. Callers should refresh the token + before expiry (or on a `401` response) by calling this endpoint again. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Any | AuthError | AuthTokenResponse + """ + + return ( + await asyncio_detailed( + client=client, + ) + ).parsed diff --git a/src/qtsurfer/api/client/_generated/models/__init__.py b/src/qtsurfer/api/client/_generated/models/__init__.py index b445297..15b48fe 100644 --- a/src/qtsurfer/api/client/_generated/models/__init__.py +++ b/src/qtsurfer/api/client/_generated/models/__init__.py @@ -1,6 +1,11 @@ """Contains all the data models used in inputs/outputs""" from .accepted_job import AcceptedJob +from .auth_error import AuthError +from .auth_error_code import AuthErrorCode +from .auth_token_response import AuthTokenResponse +from .auth_token_response_tier import AuthTokenResponseTier +from .auth_token_response_token_type import AuthTokenResponseTokenType from .backtest_job_result import BacktestJobResult from .cancel_execution_response_200 import CancelExecutionResponse200 from .cancel_execution_response_200_status import CancelExecutionResponse200Status @@ -24,6 +29,11 @@ __all__ = ( "AcceptedJob", + "AuthError", + "AuthErrorCode", + "AuthTokenResponse", + "AuthTokenResponseTier", + "AuthTokenResponseTokenType", "BacktestJobResult", "CancelExecutionResponse200", "CancelExecutionResponse200Status", diff --git a/src/qtsurfer/api/client/_generated/models/auth_error.py b/src/qtsurfer/api/client/_generated/models/auth_error.py new file mode 100644 index 0000000..3fc43c2 --- /dev/null +++ b/src/qtsurfer/api/client/_generated/models/auth_error.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.auth_error_code import AuthErrorCode + +T = TypeVar("T", bound="AuthError") + + +@_attrs_define +class AuthError: + """Error envelope returned by `POST /auth/token` when the API key is rejected. + + Attributes: + code (AuthErrorCode): Machine-readable error reason. + message (str): Human-readable description of the failure. + """ + + code: AuthErrorCode + message: str + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + code = self.code.value + + message = self.message + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "code": code, + "message": message, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + code = AuthErrorCode(d.pop("code")) + + message = d.pop("message") + + auth_error = cls( + code=code, + message=message, + ) + + auth_error.additional_properties = d + return auth_error + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/src/qtsurfer/api/client/_generated/models/auth_error_code.py b/src/qtsurfer/api/client/_generated/models/auth_error_code.py new file mode 100644 index 0000000..9ed6491 --- /dev/null +++ b/src/qtsurfer/api/client/_generated/models/auth_error_code.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class AuthErrorCode(str, Enum): + APIKEY_EXPIRED = "apikey_expired" + APIKEY_REVOKED = "apikey_revoked" + INVALID_APIKEY = "invalid_apikey" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/qtsurfer/api/client/_generated/models/auth_token_response.py b/src/qtsurfer/api/client/_generated/models/auth_token_response.py new file mode 100644 index 0000000..b703af3 --- /dev/null +++ b/src/qtsurfer/api/client/_generated/models/auth_token_response.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.auth_token_response_tier import AuthTokenResponseTier +from ..models.auth_token_response_token_type import AuthTokenResponseTokenType +from ..types import UNSET, Unset + +T = TypeVar("T", bound="AuthTokenResponse") + + +@_attrs_define +class AuthTokenResponse: + """ + Attributes: + access_token (str): Short-lived HS256 JWT. Send as `Authorization: Bearer ` on all other endpoints. + token_type (AuthTokenResponseTokenType): Always `Bearer`. + expires_in (int): Seconds until the JWT expires (typically 3600). Example: 3600. + tier (AuthTokenResponseTier): Subscription tier this token was issued for. Drives rate limits and feature flags + on downstream endpoints. Example: free. + scopes (list[str] | Unset): Scopes granted to this token. Reserved for future use; currently always empty. + """ + + access_token: str + token_type: AuthTokenResponseTokenType + expires_in: int + tier: AuthTokenResponseTier + scopes: list[str] | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + access_token = self.access_token + + token_type = self.token_type.value + + expires_in = self.expires_in + + tier = self.tier.value + + scopes: list[str] | Unset = UNSET + if not isinstance(self.scopes, Unset): + scopes = self.scopes + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "access_token": access_token, + "token_type": token_type, + "expires_in": expires_in, + "tier": tier, + } + ) + if scopes is not UNSET: + field_dict["scopes"] = scopes + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + access_token = d.pop("access_token") + + token_type = AuthTokenResponseTokenType(d.pop("token_type")) + + expires_in = d.pop("expires_in") + + tier = AuthTokenResponseTier(d.pop("tier")) + + scopes = cast(list[str], d.pop("scopes", UNSET)) + + auth_token_response = cls( + access_token=access_token, + token_type=token_type, + expires_in=expires_in, + tier=tier, + scopes=scopes, + ) + + auth_token_response.additional_properties = d + return auth_token_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/src/qtsurfer/api/client/_generated/models/auth_token_response_tier.py b/src/qtsurfer/api/client/_generated/models/auth_token_response_tier.py new file mode 100644 index 0000000..0a84abb --- /dev/null +++ b/src/qtsurfer/api/client/_generated/models/auth_token_response_tier.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class AuthTokenResponseTier(str, Enum): + BASIC = "basic" + ELITE = "elite" + FREE = "free" + PRO = "pro" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/qtsurfer/api/client/_generated/models/auth_token_response_token_type.py b/src/qtsurfer/api/client/_generated/models/auth_token_response_token_type.py new file mode 100644 index 0000000..c1fb54a --- /dev/null +++ b/src/qtsurfer/api/client/_generated/models/auth_token_response_token_type.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class AuthTokenResponseTokenType(str, Enum): + BEARER = "Bearer" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/qtsurfer/api/client/api/__init__.py b/src/qtsurfer/api/client/api/__init__.py index 96e13c9..3756a77 100644 --- a/src/qtsurfer/api/client/api/__init__.py +++ b/src/qtsurfer/api/client/api/__init__.py @@ -6,6 +6,6 @@ ``asyncio_detailed``. """ -from qtsurfer.api.client.api import backtesting, exchange, strategy +from qtsurfer.api.client.api import auth, backtesting, exchange, strategy -__all__ = ["backtesting", "exchange", "strategy"] +__all__ = ["auth", "backtesting", "exchange", "strategy"] diff --git a/src/qtsurfer/api/client/api/auth.py b/src/qtsurfer/api/client/api/auth.py new file mode 100644 index 0000000..639e605 --- /dev/null +++ b/src/qtsurfer/api/client/api/auth.py @@ -0,0 +1,5 @@ +"""Endpoints for the ``Auth`` tag — re-exported from the generated tree.""" + +from qtsurfer.api.client._generated.api.auth import auth + +__all__ = ["auth"] diff --git a/tests/test_smoke.py b/tests/test_smoke.py index cae7308..2fb9205 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -36,6 +36,7 @@ def test_known_endpoints_are_present() -> None: Update this list deliberately whenever the spec changes. """ + from qtsurfer.api.client.api.auth import auth from qtsurfer.api.client.api.backtesting import ( cancel_execution, execute_backtesting, @@ -52,6 +53,7 @@ def test_known_endpoints_are_present() -> None: from qtsurfer.api.client.api.strategy import get_strategy_status endpoints = [ + auth, get_exchanges, get_instruments, get_exchange_tickers_hour, @@ -73,6 +75,8 @@ def test_known_endpoints_are_present() -> None: def test_models_re_export() -> None: from qtsurfer.api.client.models import ( + AuthError, + AuthTokenResponse, BacktestJobResult, Exchange, InstrumentDetail, @@ -87,3 +91,5 @@ def test_models_re_export() -> None: assert BacktestJobResult is not None assert ResultMap is not None assert ResponseError is not None + assert AuthTokenResponse is not None + assert AuthError is not None diff --git a/uv.lock b/uv.lock index fb8e447..2375f2c 100644 --- a/uv.lock +++ b/uv.lock @@ -1025,7 +1025,7 @@ wheels = [ [[package]] name = "qtsurfer-api-client" -version = "0.94.0" +version = "0.95.0" source = { editable = "." } dependencies = [ { name = "attrs" }, From 9da0bbca45c83eaf635c413c4ceb1c3c15e48fe9 Mon Sep 17 00:00:00 2001 From: Manuel Polo Date: Mon, 25 May 2026 20:32:02 +0000 Subject: [PATCH 2/2] feat: regenerate client for openapi 0.95.1 (AuthError -> AuthTokenError) Picks up the AuthError schema rename in spec 0.95.1 (symmetric with AuthTokenResponse). All other contract details unchanged. Generated model files renamed: - auth_error.py -> auth_token_error.py - auth_error_code.py -> auth_token_error_code.py Public surface: client now exposes AuthTokenError (was AuthError). Smoke test updated accordingly. --- pyproject.toml | 2 +- .../api/client/_generated/api/auth/auth.py | 24 +++++++++---------- .../api/client/_generated/models/__init__.py | 8 +++---- .../{auth_error.py => auth_token_error.py} | 18 +++++++------- ...error_code.py => auth_token_error_code.py} | 2 +- tests/test_smoke.py | 4 ++-- uv.lock | 2 +- 7 files changed, 30 insertions(+), 30 deletions(-) rename src/qtsurfer/api/client/_generated/models/{auth_error.py => auth_token_error.py} (80%) rename src/qtsurfer/api/client/_generated/models/{auth_error_code.py => auth_token_error_code.py} (84%) diff --git a/pyproject.toml b/pyproject.toml index 7445856..4a6f887 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qtsurfer-api-client" -version = "0.95.0" +version = "0.95.1" description = "Auto-generated Python client for the QTSurfer API." readme = "README.md" license = "Apache-2.0" diff --git a/src/qtsurfer/api/client/_generated/api/auth/auth.py b/src/qtsurfer/api/client/_generated/api/auth/auth.py index c6c0399..f4e16a1 100644 --- a/src/qtsurfer/api/client/_generated/api/auth/auth.py +++ b/src/qtsurfer/api/client/_generated/api/auth/auth.py @@ -5,7 +5,7 @@ from ... import errors from ...client import AuthenticatedClient, Client -from ...models.auth_error import AuthError +from ...models.auth_token_error import AuthTokenError from ...models.auth_token_response import AuthTokenResponse from ...types import Response @@ -22,14 +22,14 @@ def _get_kwargs() -> dict[str, Any]: def _parse_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> Any | AuthError | AuthTokenResponse | None: +) -> Any | AuthTokenError | AuthTokenResponse | None: if response.status_code == 200: response_200 = AuthTokenResponse.from_dict(response.json()) return response_200 if response.status_code == 401: - response_401 = AuthError.from_dict(response.json()) + response_401 = AuthTokenError.from_dict(response.json()) return response_401 @@ -45,7 +45,7 @@ def _parse_response( def _build_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> Response[Any | AuthError | AuthTokenResponse]: +) -> Response[Any | AuthTokenError | AuthTokenResponse]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -57,7 +57,7 @@ def _build_response( def sync_detailed( *, client: AuthenticatedClient, -) -> Response[Any | AuthError | AuthTokenResponse]: +) -> Response[Any | AuthTokenError | AuthTokenResponse]: """Exchange API key for a short-lived JWT Exchanges a long-lived API key for a short-lived JWT used by every other @@ -74,7 +74,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Any | AuthError | AuthTokenResponse] + Response[Any | AuthTokenError | AuthTokenResponse] """ kwargs = _get_kwargs() @@ -89,7 +89,7 @@ def sync_detailed( def sync( *, client: AuthenticatedClient, -) -> Any | AuthError | AuthTokenResponse | None: +) -> Any | AuthTokenError | AuthTokenResponse | None: """Exchange API key for a short-lived JWT Exchanges a long-lived API key for a short-lived JWT used by every other @@ -106,7 +106,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Any | AuthError | AuthTokenResponse + Any | AuthTokenError | AuthTokenResponse """ return sync_detailed( @@ -117,7 +117,7 @@ def sync( async def asyncio_detailed( *, client: AuthenticatedClient, -) -> Response[Any | AuthError | AuthTokenResponse]: +) -> Response[Any | AuthTokenError | AuthTokenResponse]: """Exchange API key for a short-lived JWT Exchanges a long-lived API key for a short-lived JWT used by every other @@ -134,7 +134,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Any | AuthError | AuthTokenResponse] + Response[Any | AuthTokenError | AuthTokenResponse] """ kwargs = _get_kwargs() @@ -147,7 +147,7 @@ async def asyncio_detailed( async def asyncio( *, client: AuthenticatedClient, -) -> Any | AuthError | AuthTokenResponse | None: +) -> Any | AuthTokenError | AuthTokenResponse | None: """Exchange API key for a short-lived JWT Exchanges a long-lived API key for a short-lived JWT used by every other @@ -164,7 +164,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Any | AuthError | AuthTokenResponse + Any | AuthTokenError | AuthTokenResponse """ return ( diff --git a/src/qtsurfer/api/client/_generated/models/__init__.py b/src/qtsurfer/api/client/_generated/models/__init__.py index 15b48fe..fb727ed 100644 --- a/src/qtsurfer/api/client/_generated/models/__init__.py +++ b/src/qtsurfer/api/client/_generated/models/__init__.py @@ -1,8 +1,8 @@ """Contains all the data models used in inputs/outputs""" from .accepted_job import AcceptedJob -from .auth_error import AuthError -from .auth_error_code import AuthErrorCode +from .auth_token_error import AuthTokenError +from .auth_token_error_code import AuthTokenErrorCode from .auth_token_response import AuthTokenResponse from .auth_token_response_tier import AuthTokenResponseTier from .auth_token_response_token_type import AuthTokenResponseTokenType @@ -29,8 +29,8 @@ __all__ = ( "AcceptedJob", - "AuthError", - "AuthErrorCode", + "AuthTokenError", + "AuthTokenErrorCode", "AuthTokenResponse", "AuthTokenResponseTier", "AuthTokenResponseTokenType", diff --git a/src/qtsurfer/api/client/_generated/models/auth_error.py b/src/qtsurfer/api/client/_generated/models/auth_token_error.py similarity index 80% rename from src/qtsurfer/api/client/_generated/models/auth_error.py rename to src/qtsurfer/api/client/_generated/models/auth_token_error.py index 3fc43c2..132bbc2 100644 --- a/src/qtsurfer/api/client/_generated/models/auth_error.py +++ b/src/qtsurfer/api/client/_generated/models/auth_token_error.py @@ -6,21 +6,21 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field -from ..models.auth_error_code import AuthErrorCode +from ..models.auth_token_error_code import AuthTokenErrorCode -T = TypeVar("T", bound="AuthError") +T = TypeVar("T", bound="AuthTokenError") @_attrs_define -class AuthError: +class AuthTokenError: """Error envelope returned by `POST /auth/token` when the API key is rejected. Attributes: - code (AuthErrorCode): Machine-readable error reason. + code (AuthTokenErrorCode): Machine-readable error reason. message (str): Human-readable description of the failure. """ - code: AuthErrorCode + code: AuthTokenErrorCode message: str additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -43,17 +43,17 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) - code = AuthErrorCode(d.pop("code")) + code = AuthTokenErrorCode(d.pop("code")) message = d.pop("message") - auth_error = cls( + auth_token_error = cls( code=code, message=message, ) - auth_error.additional_properties = d - return auth_error + auth_token_error.additional_properties = d + return auth_token_error @property def additional_keys(self) -> list[str]: diff --git a/src/qtsurfer/api/client/_generated/models/auth_error_code.py b/src/qtsurfer/api/client/_generated/models/auth_token_error_code.py similarity index 84% rename from src/qtsurfer/api/client/_generated/models/auth_error_code.py rename to src/qtsurfer/api/client/_generated/models/auth_token_error_code.py index 9ed6491..0253f36 100644 --- a/src/qtsurfer/api/client/_generated/models/auth_error_code.py +++ b/src/qtsurfer/api/client/_generated/models/auth_token_error_code.py @@ -1,7 +1,7 @@ from enum import Enum -class AuthErrorCode(str, Enum): +class AuthTokenErrorCode(str, Enum): APIKEY_EXPIRED = "apikey_expired" APIKEY_REVOKED = "apikey_revoked" INVALID_APIKEY = "invalid_apikey" diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 2fb9205..ba5806c 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -75,7 +75,7 @@ def test_known_endpoints_are_present() -> None: def test_models_re_export() -> None: from qtsurfer.api.client.models import ( - AuthError, + AuthTokenError, AuthTokenResponse, BacktestJobResult, Exchange, @@ -92,4 +92,4 @@ def test_models_re_export() -> None: assert ResultMap is not None assert ResponseError is not None assert AuthTokenResponse is not None - assert AuthError is not None + assert AuthTokenError is not None diff --git a/uv.lock b/uv.lock index 2375f2c..fcd6ea8 100644 --- a/uv.lock +++ b/uv.lock @@ -1025,7 +1025,7 @@ wheels = [ [[package]] name = "qtsurfer-api-client" -version = "0.95.0" +version = "0.95.1" source = { editable = "." } dependencies = [ { name = "attrs" },