From efebd12e762638162b212ec2c59f3774f38c0f22 Mon Sep 17 00:00:00 2001 From: NathanFallet Date: Wed, 21 Oct 2026 17:38:17 +0100 Subject: [PATCH 1/2] feat: regenerate with the from/to filters and the orders labels route --- .spec-sha256 | 2 +- README.md | 2 +- src/controlresell/client.py | 67 +++++- .../list_conversation_messages.py | 53 ++++- .../api/conversations/list_conversations.py | 57 ++++- .../api/orders/list_labels_to_ship.py | 224 ++++++++++++++++++ .../generated/models/order_label.py | 8 + tests/test_client.py | 28 +++ 8 files changed, 420 insertions(+), 21 deletions(-) create mode 100644 src/controlresell/generated/api/orders/list_labels_to_ship.py diff --git a/.spec-sha256 b/.spec-sha256 index b5b7f17..c86c3d8 100644 --- a/.spec-sha256 +++ b/.spec-sha256 @@ -1 +1 @@ -1acbf02fb5996004ee72495da2440677842bc7d8a705b9a4a0741565f353df4e +1c648bd5dd3dab534723d1cb26e19fdbfcc7359cd8a33a1f8d99e5688e4ba54f diff --git a/README.md b/README.md index 6f4d88e..d3bff14 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ client = ControlResell(api_key="cr_live_...") # or set CONTROLRESELL_API_KEY me = client.me.get() for item in client.items.iter(sold=False): print(item.name) -for order in client.orders.iter(bucket="to_send"): # needs the orders:read scope; also orders.get(id), orders.labels(id) +for order in client.orders.iter(bucket="to_send"): # needs the orders:read scope; also orders.get(id), orders.labels(id), orders.iter_labels_to_ship() print(order.id) # Writes (API key with `items:write` / `conversations:write`); `idempotency_key=` chooses the key. diff --git a/src/controlresell/client.py b/src/controlresell/client.py index e814ede..ee2b322 100644 --- a/src/controlresell/client.py +++ b/src/controlresell/client.py @@ -34,7 +34,7 @@ update_item, ) from .generated.api.me import get_me -from .generated.api.orders import get_order, list_order_labels, list_orders +from .generated.api.orders import get_order, list_labels_to_ship, list_order_labels, list_orders from .generated.client import AuthenticatedClient from .generated.models import ( Account, @@ -50,6 +50,7 @@ Message, MessagePage, Order, + OrderLabel, OrderLabelPage, OrderPage, PriceDropRequest, @@ -250,20 +251,39 @@ def list( cursor: str | None = None, unread: bool | None = None, account_id: UUID | str | None = None, + from_: datetime.datetime | None = None, + to: datetime.datetime | None = None, ) -> ConversationPage: - """One page of the conversations with buyers.""" + """One page of the conversations with buyers. + + ``from_`` (inclusive) and ``to`` (exclusive) bound the last message (a naive datetime is read as UTC). + """ account = UUID(str(account_id)) if account_id is not None else None return self._sdk._call( # noqa: SLF001 - list_conversations, **_query(limit=limit, cursor=cursor, unread=unread, account_id=account) + list_conversations, + **_query(limit=limit, cursor=cursor, unread=unread, account_id=account, from_=_utc(from_), to=_utc(to)), ) def get(self, id: UUID | str) -> Conversation: return self._sdk._call(get_conversation, id=UUID(str(id))) # noqa: SLF001 - def messages(self, id: UUID | str, *, limit: int | None = None, cursor: str | None = None) -> MessagePage: - """One page of the messages of a conversation, oldest first.""" + def messages( + self, + id: UUID | str, + *, + limit: int | None = None, + cursor: str | None = None, + from_: datetime.datetime | None = None, + to: datetime.datetime | None = None, + ) -> MessagePage: + """One page of the messages of a conversation, oldest first. + + ``from_`` (inclusive) and ``to`` (exclusive) bound the period (a naive datetime is read as UTC). + """ return self._sdk._call( # noqa: SLF001 - list_conversation_messages, id=UUID(str(id)), **_query(limit=limit, cursor=cursor) + list_conversation_messages, + id=UUID(str(id)), + **_query(limit=limit, cursor=cursor, from_=_utc(from_), to=_utc(to)), ) def send_message(self, id: UUID | str, body: SendMessageRequest, *, idempotency_key: str | None = None) -> Message: @@ -279,14 +299,31 @@ def send_offer(self, id: UUID | str, body: SendOfferRequest, *, idempotency_key: ) def iter( - self, *, limit: int | None = None, unread: bool | None = None, account_id: UUID | str | None = None + self, + *, + limit: int | None = None, + unread: bool | None = None, + account_id: UUID | str | None = None, + from_: datetime.datetime | None = None, + to: datetime.datetime | None = None, ) -> Iterator[Conversation]: """Every conversation, page after page, with the same filters.""" - return paginate(lambda cursor: self.list(limit=limit, cursor=cursor, unread=unread, account_id=account_id)) + return paginate( + lambda cursor: self.list( + limit=limit, cursor=cursor, unread=unread, account_id=account_id, from_=from_, to=to + ) + ) - def iter_messages(self, id: UUID | str, *, limit: int | None = None) -> Iterator[Any]: - """Every message of a conversation, page after page.""" - return paginate(lambda cursor: self.messages(id, limit=limit, cursor=cursor)) + def iter_messages( + self, + id: UUID | str, + *, + limit: int | None = None, + from_: datetime.datetime | None = None, + to: datetime.datetime | None = None, + ) -> Iterator[Any]: + """Every message of a conversation, page after page, with the same period.""" + return paginate(lambda cursor: self.messages(id, limit=limit, cursor=cursor, from_=from_, to=to)) class _Orders: @@ -325,6 +362,14 @@ def labels(self, id: UUID | str) -> OrderLabelPage: """The shipping labels of an order.""" return self._sdk._call(list_order_labels, id=UUID(str(id))) # noqa: SLF001 + def labels_to_ship(self, *, limit: int | None = None, cursor: str | None = None) -> OrderLabelPage: + """One page of the labels still to ship over all the orders, the oldest order first (each names its order).""" + return self._sdk._call(list_labels_to_ship, **_query(limit=limit, cursor=cursor)) # noqa: SLF001 + + def iter_labels_to_ship(self, *, limit: int | None = None) -> Iterator[OrderLabel]: + """Every label still to ship, page after page.""" + return paginate(lambda cursor: self.labels_to_ship(limit=limit, cursor=cursor)) + def iter( self, *, diff --git a/src/controlresell/generated/api/conversations/list_conversation_messages.py b/src/controlresell/generated/api/conversations/list_conversation_messages.py index ca12ad9..b098df4 100644 --- a/src/controlresell/generated/api/conversations/list_conversation_messages.py +++ b/src/controlresell/generated/api/conversations/list_conversation_messages.py @@ -1,3 +1,4 @@ +import datetime from http import HTTPStatus from typing import Any from urllib.parse import quote @@ -16,6 +17,8 @@ def _get_kwargs( *, limit: int | Unset = 50, cursor: str | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> dict[str, Any]: params: dict[str, Any] = {} @@ -24,6 +27,16 @@ def _get_kwargs( params["cursor"] = cursor + json_from_: str | Unset = UNSET + if not isinstance(from_, Unset): + json_from_ = from_.isoformat() + params["from"] = json_from_ + + json_to: str | Unset = UNSET + if not isinstance(to, Unset): + json_to = to.isoformat() + params["to"] = json_to + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} _kwargs: dict[str, Any] = { @@ -102,16 +115,22 @@ def sync_detailed( client: AuthenticatedClient | Client, limit: int | Unset = 50, cursor: str | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> Response[ErrorResponse | MessagePage]: """List the messages of a conversation The messages of one conversation, oldest first, one page at a time: at most its 1 000 most recent - messages. Requires `conversations:read`. + messages (of the period, with `from` or `to`). Pass the `nextCursor` of a page as `cursor` with the + same `from` and `to` (a cursor reused with others is an `invalid_cursor`). `from` and `to` go + straight to a period: `from` is inclusive, `to` exclusive. Requires `conversations:read`. Args: id (UUID): limit (int | Unset): Default: 50. cursor (str | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -125,6 +144,8 @@ def sync_detailed( id=id, limit=limit, cursor=cursor, + from_=from_, + to=to, ) response = client.get_httpx_client().request( @@ -140,16 +161,22 @@ def sync( client: AuthenticatedClient | Client, limit: int | Unset = 50, cursor: str | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> ErrorResponse | MessagePage | None: """List the messages of a conversation The messages of one conversation, oldest first, one page at a time: at most its 1 000 most recent - messages. Requires `conversations:read`. + messages (of the period, with `from` or `to`). Pass the `nextCursor` of a page as `cursor` with the + same `from` and `to` (a cursor reused with others is an `invalid_cursor`). `from` and `to` go + straight to a period: `from` is inclusive, `to` exclusive. Requires `conversations:read`. Args: id (UUID): limit (int | Unset): Default: 50. cursor (str | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -164,6 +191,8 @@ def sync( client=client, limit=limit, cursor=cursor, + from_=from_, + to=to, ).parsed @@ -173,16 +202,22 @@ async def asyncio_detailed( client: AuthenticatedClient | Client, limit: int | Unset = 50, cursor: str | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> Response[ErrorResponse | MessagePage]: """List the messages of a conversation The messages of one conversation, oldest first, one page at a time: at most its 1 000 most recent - messages. Requires `conversations:read`. + messages (of the period, with `from` or `to`). Pass the `nextCursor` of a page as `cursor` with the + same `from` and `to` (a cursor reused with others is an `invalid_cursor`). `from` and `to` go + straight to a period: `from` is inclusive, `to` exclusive. Requires `conversations:read`. Args: id (UUID): limit (int | Unset): Default: 50. cursor (str | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -196,6 +231,8 @@ async def asyncio_detailed( id=id, limit=limit, cursor=cursor, + from_=from_, + to=to, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -209,16 +246,22 @@ async def asyncio( client: AuthenticatedClient | Client, limit: int | Unset = 50, cursor: str | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> ErrorResponse | MessagePage | None: """List the messages of a conversation The messages of one conversation, oldest first, one page at a time: at most its 1 000 most recent - messages. Requires `conversations:read`. + messages (of the period, with `from` or `to`). Pass the `nextCursor` of a page as `cursor` with the + same `from` and `to` (a cursor reused with others is an `invalid_cursor`). `from` and `to` go + straight to a period: `from` is inclusive, `to` exclusive. Requires `conversations:read`. Args: id (UUID): limit (int | Unset): Default: 50. cursor (str | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -234,5 +277,7 @@ async def asyncio( client=client, limit=limit, cursor=cursor, + from_=from_, + to=to, ) ).parsed diff --git a/src/controlresell/generated/api/conversations/list_conversations.py b/src/controlresell/generated/api/conversations/list_conversations.py index 33f2c07..fc237ab 100644 --- a/src/controlresell/generated/api/conversations/list_conversations.py +++ b/src/controlresell/generated/api/conversations/list_conversations.py @@ -1,3 +1,4 @@ +import datetime from http import HTTPStatus from typing import Any from uuid import UUID @@ -16,6 +17,8 @@ def _get_kwargs( cursor: str | Unset = UNSET, unread: bool | Unset = UNSET, account_id: UUID | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> dict[str, Any]: params: dict[str, Any] = {} @@ -31,6 +34,16 @@ def _get_kwargs( json_account_id = str(account_id) params["accountId"] = json_account_id + json_from_: str | Unset = UNSET + if not isinstance(from_, Unset): + json_from_ = from_.isoformat() + params["from"] = json_from_ + + json_to: str | Unset = UNSET + if not isinstance(to, Unset): + json_to = to.isoformat() + params["to"] = json_to + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} _kwargs: dict[str, Any] = { @@ -103,18 +116,25 @@ def sync_detailed( cursor: str | Unset = UNSET, unread: bool | Unset = UNSET, account_id: UUID | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> Response[ConversationPage | ErrorResponse]: """List the seller's conversations The conversations with buyers on the seller's marketplace accounts, latest activity first, one page at a time. A conversation that receives a message while you read moves to the head: read again from - the first page to see it. Requires `conversations:read`. + the first page to see it. Pass the `nextCursor` of a page as `cursor` with the same filters (a + cursor reused with other filters is an `invalid_cursor`). `from` and `to` go straight to a period of + the last message: `from` is inclusive, `to` exclusive, and the period is read page by page like the + whole list. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. cursor (str | Unset): unread (bool | Unset): account_id (UUID | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -129,6 +149,8 @@ def sync_detailed( cursor=cursor, unread=unread, account_id=account_id, + from_=from_, + to=to, ) response = client.get_httpx_client().request( @@ -145,18 +167,25 @@ def sync( cursor: str | Unset = UNSET, unread: bool | Unset = UNSET, account_id: UUID | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> ConversationPage | ErrorResponse | None: """List the seller's conversations The conversations with buyers on the seller's marketplace accounts, latest activity first, one page at a time. A conversation that receives a message while you read moves to the head: read again from - the first page to see it. Requires `conversations:read`. + the first page to see it. Pass the `nextCursor` of a page as `cursor` with the same filters (a + cursor reused with other filters is an `invalid_cursor`). `from` and `to` go straight to a period of + the last message: `from` is inclusive, `to` exclusive, and the period is read page by page like the + whole list. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. cursor (str | Unset): unread (bool | Unset): account_id (UUID | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -172,6 +201,8 @@ def sync( cursor=cursor, unread=unread, account_id=account_id, + from_=from_, + to=to, ).parsed @@ -182,18 +213,25 @@ async def asyncio_detailed( cursor: str | Unset = UNSET, unread: bool | Unset = UNSET, account_id: UUID | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> Response[ConversationPage | ErrorResponse]: """List the seller's conversations The conversations with buyers on the seller's marketplace accounts, latest activity first, one page at a time. A conversation that receives a message while you read moves to the head: read again from - the first page to see it. Requires `conversations:read`. + the first page to see it. Pass the `nextCursor` of a page as `cursor` with the same filters (a + cursor reused with other filters is an `invalid_cursor`). `from` and `to` go straight to a period of + the last message: `from` is inclusive, `to` exclusive, and the period is read page by page like the + whole list. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. cursor (str | Unset): unread (bool | Unset): account_id (UUID | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -208,6 +246,8 @@ async def asyncio_detailed( cursor=cursor, unread=unread, account_id=account_id, + from_=from_, + to=to, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -222,18 +262,25 @@ async def asyncio( cursor: str | Unset = UNSET, unread: bool | Unset = UNSET, account_id: UUID | Unset = UNSET, + from_: datetime.datetime | Unset = UNSET, + to: datetime.datetime | Unset = UNSET, ) -> ConversationPage | ErrorResponse | None: """List the seller's conversations The conversations with buyers on the seller's marketplace accounts, latest activity first, one page at a time. A conversation that receives a message while you read moves to the head: read again from - the first page to see it. Requires `conversations:read`. + the first page to see it. Pass the `nextCursor` of a page as `cursor` with the same filters (a + cursor reused with other filters is an `invalid_cursor`). `from` and `to` go straight to a period of + the last message: `from` is inclusive, `to` exclusive, and the period is read page by page like the + whole list. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. cursor (str | Unset): unread (bool | Unset): account_id (UUID | Unset): + from_ (datetime.datetime | Unset): + to (datetime.datetime | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -250,5 +297,7 @@ async def asyncio( cursor=cursor, unread=unread, account_id=account_id, + from_=from_, + to=to, ) ).parsed diff --git a/src/controlresell/generated/api/orders/list_labels_to_ship.py b/src/controlresell/generated/api/orders/list_labels_to_ship.py new file mode 100644 index 0000000..7f82f57 --- /dev/null +++ b/src/controlresell/generated/api/orders/list_labels_to_ship.py @@ -0,0 +1,224 @@ +from http import HTTPStatus +from typing import Any + +import httpx + +from ...client import AuthenticatedClient, Client +from ...models.error_response import ErrorResponse +from ...models.order_label_page import OrderLabelPage +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + *, + limit: int | Unset = 50, + cursor: str | Unset = UNSET, +) -> dict[str, Any]: + + params: dict[str, Any] = {} + + params["limit"] = limit + + params["cursor"] = cursor + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/api/public/v1/orders/labels", + "params": params, + } + + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> ErrorResponse | OrderLabelPage: + if response.status_code == 200: + response_200 = OrderLabelPage.from_dict(response.json()) + + return response_200 + + if response.status_code == 400: + response_400 = ErrorResponse.from_dict(response.json()) + + return response_400 + + if response.status_code == 401: + response_401 = ErrorResponse.from_dict(response.json()) + + return response_401 + + if response.status_code == 402: + response_402 = ErrorResponse.from_dict(response.json()) + + return response_402 + + if response.status_code == 403: + response_403 = ErrorResponse.from_dict(response.json()) + + return response_403 + + if response.status_code == 429: + response_429 = ErrorResponse.from_dict(response.json()) + + return response_429 + + if response.status_code == 500: + response_500 = ErrorResponse.from_dict(response.json()) + + return response_500 + + response_default = ErrorResponse.from_dict(response.json()) + + return response_default + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[ErrorResponse | OrderLabelPage]: + 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 | Client, + limit: int | Unset = 50, + cursor: str | Unset = UNSET, +) -> Response[ErrorResponse | OrderLabelPage]: + """List the shipping labels still to ship + + The shipping labels of the seller's orders waiting to be shipped (label ordered or sent to the + seller, order not marked prepared), oldest order first, one page at a time. Each label names its + order. The links are signed and expire: read them again rather than storing them. Requires + `orders:read`. + + Args: + limit (int | Unset): Default: 50. + cursor (str | Unset): + + 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[ErrorResponse | OrderLabelPage] + """ + + kwargs = _get_kwargs( + limit=limit, + cursor=cursor, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient | Client, + limit: int | Unset = 50, + cursor: str | Unset = UNSET, +) -> ErrorResponse | OrderLabelPage | None: + """List the shipping labels still to ship + + The shipping labels of the seller's orders waiting to be shipped (label ordered or sent to the + seller, order not marked prepared), oldest order first, one page at a time. Each label names its + order. The links are signed and expire: read them again rather than storing them. Requires + `orders:read`. + + Args: + limit (int | Unset): Default: 50. + cursor (str | Unset): + + 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: + ErrorResponse | OrderLabelPage + """ + + return sync_detailed( + client=client, + limit=limit, + cursor=cursor, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, + limit: int | Unset = 50, + cursor: str | Unset = UNSET, +) -> Response[ErrorResponse | OrderLabelPage]: + """List the shipping labels still to ship + + The shipping labels of the seller's orders waiting to be shipped (label ordered or sent to the + seller, order not marked prepared), oldest order first, one page at a time. Each label names its + order. The links are signed and expire: read them again rather than storing them. Requires + `orders:read`. + + Args: + limit (int | Unset): Default: 50. + cursor (str | Unset): + + 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[ErrorResponse | OrderLabelPage] + """ + + kwargs = _get_kwargs( + limit=limit, + cursor=cursor, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient | Client, + limit: int | Unset = 50, + cursor: str | Unset = UNSET, +) -> ErrorResponse | OrderLabelPage | None: + """List the shipping labels still to ship + + The shipping labels of the seller's orders waiting to be shipped (label ordered or sent to the + seller, order not marked prepared), oldest order first, one page at a time. Each label names its + order. The links are signed and expire: read them again rather than storing them. Requires + `orders:read`. + + Args: + limit (int | Unset): Default: 50. + cursor (str | Unset): + + 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: + ErrorResponse | OrderLabelPage + """ + + return ( + await asyncio_detailed( + client=client, + limit=limit, + cursor=cursor, + ) + ).parsed diff --git a/src/controlresell/generated/models/order_label.py b/src/controlresell/generated/models/order_label.py index 64a10cc..106f48a 100644 --- a/src/controlresell/generated/models/order_label.py +++ b/src/controlresell/generated/models/order_label.py @@ -18,6 +18,7 @@ class OrderLabel: """ Attributes: id (UUID): + order_id (UUID): carrier (None | str | Unset): url (None | str | Unset): original_url (None | str | Unset): @@ -25,6 +26,7 @@ class OrderLabel: """ id: UUID + order_id: UUID carrier: None | str | Unset = UNSET url: None | str | Unset = UNSET original_url: None | str | Unset = UNSET @@ -34,6 +36,8 @@ class OrderLabel: def to_dict(self) -> dict[str, Any]: id = str(self.id) + order_id = str(self.order_id) + carrier: None | str | Unset if isinstance(self.carrier, Unset): carrier = UNSET @@ -65,6 +69,7 @@ def to_dict(self) -> dict[str, Any]: field_dict.update( { "id": id, + "orderId": order_id, } ) if carrier is not UNSET: @@ -83,6 +88,8 @@ def from_dict(cls, src_dict: Mapping[str, Any]) -> Self: d = dict(src_dict) id = UUID(d.pop("id")) + order_id = UUID(d.pop("orderId")) + def _parse_carrier(data: object) -> None | str | Unset: if data is None: return data @@ -129,6 +136,7 @@ def _parse_created_at(data: object) -> datetime.datetime | None | Unset: order_label = cls( id=id, + order_id=order_id, carrier=carrier, url=url, original_url=original_url, diff --git a/tests/test_client.py b/tests/test_client.py index e901210..5bc295e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -313,3 +313,31 @@ def test_orders_list_reads_a_naive_datetime_as_utc(): sdk.orders.list(from_=datetime.datetime(2026, 10, 1), to=datetime.datetime(2026, 10, 2, tzinfo=datetime.timezone.utc)) params = requests[0].url.params assert params["from"] == "2026-10-01T00:00:00+00:00" and params["to"] == "2026-10-02T00:00:00+00:00" + + +def test_orders_iter_labels_to_ship_follows_the_cursor_on_orders_labels(): + seen = [] + + def handler(request): + seen.append(request.url) + if request.url.params.get("cursor"): + return reply({"data": [{"id": ACCOUNT, "orderId": ACCOUNT}], "nextCursor": None}) + return reply({"data": [{"id": ACCOUNT, "orderId": ACCOUNT}], "nextCursor": "c2"}) + + _, sdk = client(handler) + labels = list(sdk.orders.iter_labels_to_ship(limit=1)) + assert len(labels) == 2 and str(labels[0].order_id) == ACCOUNT + assert [u.path for u in seen] == ["/api/public/v1/orders/labels"] * 2 + assert seen[1].params["cursor"] == "c2" + + +def test_conversations_messages_and_list_send_the_from_and_to_window(): + import datetime + + requests, sdk = client(lambda r: reply({"data": [], "nextCursor": None})) + start = datetime.datetime(2026, 10, 1, tzinfo=datetime.timezone.utc) + sdk.conversations.messages(ACCOUNT, from_=start, to=datetime.datetime(2026, 10, 2)) + sdk.conversations.list(from_=start) + first, second = (r.url.params for r in requests) + assert first["from"] == "2026-10-01T00:00:00+00:00" and first["to"] == "2026-10-02T00:00:00+00:00" + assert second["from"] == "2026-10-01T00:00:00+00:00" and "to" not in second From b2ac9abde4224284120bcddee434ed2073c8c854 Mon Sep 17 00:00:00 2001 From: NathanFallet Date: Wed, 21 Oct 2026 17:45:50 +0100 Subject: [PATCH 2/2] Leave the README alone (ENG-055) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3bff14..6f4d88e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ client = ControlResell(api_key="cr_live_...") # or set CONTROLRESELL_API_KEY me = client.me.get() for item in client.items.iter(sold=False): print(item.name) -for order in client.orders.iter(bucket="to_send"): # needs the orders:read scope; also orders.get(id), orders.labels(id), orders.iter_labels_to_ship() +for order in client.orders.iter(bucket="to_send"): # needs the orders:read scope; also orders.get(id), orders.labels(id) print(order.id) # Writes (API key with `items:write` / `conversations:write`); `idempotency_key=` chooses the key.