From a651895c7735e9d1c17b4515f79917b5808bb504 Mon Sep 17 00:00:00 2001 From: NathanFallet Date: Wed, 28 Oct 2026 17:04:17 +0100 Subject: [PATCH] feat: regenerate from the production spec (optional scheduledAt on publish, price drop and republish) The spec served by production now documents scheduledAt on the three write requests (at most 30 days ahead). The hand-written client passes the request through, so no wrapper changed; a test, the README and the javadoc show the field. No major bump: the major stays the API version. Co-Authored-By: Claude Sonnet 5.5 --- .spec-sha256 | 2 +- README.md | 3 ++ .../api/conversations/list_conversations.py | 28 +++++++---- .../generated/api/items/drop_item_price.py | 32 ++++++------- .../generated/api/items/publish_item.py | 44 +++++++++-------- .../generated/api/items/republish_item.py | 48 +++++++++---------- .../generated/models/price_drop_request.py | 31 ++++++++++++ .../generated/models/publish_request.py | 31 ++++++++++++ .../generated/models/republish_request.py | 31 ++++++++++++ tests/test_client.py | 15 ++++++ 10 files changed, 196 insertions(+), 69 deletions(-) diff --git a/.spec-sha256 b/.spec-sha256 index 3c42680..ebd3a33 100644 --- a/.spec-sha256 +++ b/.spec-sha256 @@ -1 +1 @@ -d95f1962bb73a3e9792c2de01bd086edce7d3c1371ece42f7c4f12032cd29e17 +06a7c92e23df7c44f8898a2b18abec43aa5c14b8f12ea10e1ce65a94a14a0fb4 diff --git a/README.md b/README.md index 4beb5db..8a3b8a9 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,14 @@ for favorite in client.favorites.iter(item_id=42): # the buyers who favorited a client.conversations.list(buyer_id="", offer_status="PENDING") # buyer_id, item_id and offer_status filter the conversations # Writes (API key with `items:write` / `conversations:write`); `idempotency_key=` chooses the key. +from datetime import datetime, timezone + from controlresell.generated.models import ItemCreateRequest, ItemUpdateRequest, PublishRequest, SendMessageRequest client.items.create(ItemCreateRequest(name="Jacket")) # client.items.update(42, ItemUpdateRequest(...)) patches client.items.publish(42, PublishRequest(account_ids=[""])) +client.items.publish(42, PublishRequest(account_ids=[""], scheduled_at=datetime(2026, 11, 2, 10, 0, tzinfo=timezone.utc))) # publish, price_drop and republish take an optional scheduled_at, at most 30 days ahead; without it they run now client.conversations.send_message("", SendMessageRequest(text="Hello!")) # Webhooks (`webhooks:read` / `webhooks:write`): endpoints, endpoint, create_endpoint, update_endpoint, delete_endpoint, diff --git a/src/controlresell/generated/api/conversations/list_conversations.py b/src/controlresell/generated/api/conversations/list_conversations.py index 7514242..aa164ec 100644 --- a/src/controlresell/generated/api/conversations/list_conversations.py +++ b/src/controlresell/generated/api/conversations/list_conversations.py @@ -142,8 +142,11 @@ def sync_detailed( the buyer's identifier on the marketplace (a conversation's `buyerId`); `itemId` keeps the conversations about the item, through their transactions on the marketplace (a conversation the marketplace ties to no listing of the item is not found); `offerStatus` keeps those with at least - one offer currently at that status, and with `itemId` that same offer is on the item. An `itemId` - that is not one of the seller's items answers an empty page. Requires `conversations:read`. + one offer currently at that status; with `itemId`, that offer is on the item through its own + transaction, and an offer with no transaction of its own counts when the conversation's transactions + carry the item (so the result is approximate for such an offer: it can be an offer about another + item of the same conversation). An `itemId` that is not one of the seller's items answers an empty + page. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. @@ -207,8 +210,11 @@ def sync( the buyer's identifier on the marketplace (a conversation's `buyerId`); `itemId` keeps the conversations about the item, through their transactions on the marketplace (a conversation the marketplace ties to no listing of the item is not found); `offerStatus` keeps those with at least - one offer currently at that status, and with `itemId` that same offer is on the item. An `itemId` - that is not one of the seller's items answers an empty page. Requires `conversations:read`. + one offer currently at that status; with `itemId`, that offer is on the item through its own + transaction, and an offer with no transaction of its own counts when the conversation's transactions + carry the item (so the result is approximate for such an offer: it can be an offer about another + item of the same conversation). An `itemId` that is not one of the seller's items answers an empty + page. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. @@ -267,8 +273,11 @@ async def asyncio_detailed( the buyer's identifier on the marketplace (a conversation's `buyerId`); `itemId` keeps the conversations about the item, through their transactions on the marketplace (a conversation the marketplace ties to no listing of the item is not found); `offerStatus` keeps those with at least - one offer currently at that status, and with `itemId` that same offer is on the item. An `itemId` - that is not one of the seller's items answers an empty page. Requires `conversations:read`. + one offer currently at that status; with `itemId`, that offer is on the item through its own + transaction, and an offer with no transaction of its own counts when the conversation's transactions + carry the item (so the result is approximate for such an offer: it can be an offer about another + item of the same conversation). An `itemId` that is not one of the seller's items answers an empty + page. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. @@ -330,8 +339,11 @@ async def asyncio( the buyer's identifier on the marketplace (a conversation's `buyerId`); `itemId` keeps the conversations about the item, through their transactions on the marketplace (a conversation the marketplace ties to no listing of the item is not found); `offerStatus` keeps those with at least - one offer currently at that status, and with `itemId` that same offer is on the item. An `itemId` - that is not one of the seller's items answers an empty page. Requires `conversations:read`. + one offer currently at that status; with `itemId`, that offer is on the item through its own + transaction, and an offer with no transaction of its own counts when the conversation's transactions + carry the item (so the result is approximate for such an offer: it can be an offer about another + item of the same conversation). An `itemId` that is not one of the seller's items answers an empty + page. Requires `conversations:read`. Args: limit (int | Unset): Default: 50. diff --git a/src/controlresell/generated/api/items/drop_item_price.py b/src/controlresell/generated/api/items/drop_item_price.py index 10c2775..e16f0ee 100644 --- a/src/controlresell/generated/api/items/drop_item_price.py +++ b/src/controlresell/generated/api/items/drop_item_price.py @@ -118,10 +118,10 @@ def sync_detailed( ) -> Response[ErrorResponse | ScheduledAction]: """Lower the price of an item's listings - Schedules a price drop of the item's existing listings, now, like the price drop of the app: the - listings are updated, not republished. `value` is what is removed from each listing's current price, - never the new price. Without `accountIds`, every account the item is online on. Requires - `items:write` and an `Idempotency-Key`. + Schedules a price drop of the item's existing listings, now or at `scheduledAt` (at most 30 days + ahead), like the price drop of the app: the listings are updated, not republished. `value` is what + is removed from each listing's current price, never the new price. Without `accountIds`, every + account the item is online on. Requires `items:write` and an `Idempotency-Key`. Args: id (int): @@ -158,10 +158,10 @@ def sync( ) -> ErrorResponse | ScheduledAction | None: """Lower the price of an item's listings - Schedules a price drop of the item's existing listings, now, like the price drop of the app: the - listings are updated, not republished. `value` is what is removed from each listing's current price, - never the new price. Without `accountIds`, every account the item is online on. Requires - `items:write` and an `Idempotency-Key`. + Schedules a price drop of the item's existing listings, now or at `scheduledAt` (at most 30 days + ahead), like the price drop of the app: the listings are updated, not republished. `value` is what + is removed from each listing's current price, never the new price. Without `accountIds`, every + account the item is online on. Requires `items:write` and an `Idempotency-Key`. Args: id (int): @@ -193,10 +193,10 @@ async def asyncio_detailed( ) -> Response[ErrorResponse | ScheduledAction]: """Lower the price of an item's listings - Schedules a price drop of the item's existing listings, now, like the price drop of the app: the - listings are updated, not republished. `value` is what is removed from each listing's current price, - never the new price. Without `accountIds`, every account the item is online on. Requires - `items:write` and an `Idempotency-Key`. + Schedules a price drop of the item's existing listings, now or at `scheduledAt` (at most 30 days + ahead), like the price drop of the app: the listings are updated, not republished. `value` is what + is removed from each listing's current price, never the new price. Without `accountIds`, every + account the item is online on. Requires `items:write` and an `Idempotency-Key`. Args: id (int): @@ -231,10 +231,10 @@ async def asyncio( ) -> ErrorResponse | ScheduledAction | None: """Lower the price of an item's listings - Schedules a price drop of the item's existing listings, now, like the price drop of the app: the - listings are updated, not republished. `value` is what is removed from each listing's current price, - never the new price. Without `accountIds`, every account the item is online on. Requires - `items:write` and an `Idempotency-Key`. + Schedules a price drop of the item's existing listings, now or at `scheduledAt` (at most 30 days + ahead), like the price drop of the app: the listings are updated, not republished. `value` is what + is removed from each listing's current price, never the new price. Without `accountIds`, every + account the item is online on. Requires `items:write` and an `Idempotency-Key`. Args: id (int): diff --git a/src/controlresell/generated/api/items/publish_item.py b/src/controlresell/generated/api/items/publish_item.py index 82571a5..e5c28bc 100644 --- a/src/controlresell/generated/api/items/publish_item.py +++ b/src/controlresell/generated/api/items/publish_item.py @@ -118,11 +118,12 @@ def sync_detailed( ) -> Response[ErrorResponse | ScheduledAction]: """Publish an item on the seller's accounts - Schedules the publication of one item of the seller's stock on the given marketplace accounts, now. - The publication runs on the account's browser: read its result on `GET /items/{id}/listings`. To - cross-list an item ("list it on eBay"), call this route with that account: this API never relays a - marketplace's own API. Publishing one item on several accounts of the same marketplace risks a - strike from it: name only the accounts you mean. Requires `items:write` and an `Idempotency-Key`. + Schedules the publication of one item of the seller's stock on the given marketplace accounts, now + or at `scheduledAt` (at most 30 days ahead). The publication runs on the account's browser: read its + result on `GET /items/{id}/listings`. To cross-list an item ("list it on eBay"), call this route + with that account: this API never relays a marketplace's own API. Publishing one item on several + accounts of the same marketplace risks a strike from it: name only the accounts you mean. Requires + `items:write` and an `Idempotency-Key`. Args: id (int): @@ -159,11 +160,12 @@ def sync( ) -> ErrorResponse | ScheduledAction | None: """Publish an item on the seller's accounts - Schedules the publication of one item of the seller's stock on the given marketplace accounts, now. - The publication runs on the account's browser: read its result on `GET /items/{id}/listings`. To - cross-list an item ("list it on eBay"), call this route with that account: this API never relays a - marketplace's own API. Publishing one item on several accounts of the same marketplace risks a - strike from it: name only the accounts you mean. Requires `items:write` and an `Idempotency-Key`. + Schedules the publication of one item of the seller's stock on the given marketplace accounts, now + or at `scheduledAt` (at most 30 days ahead). The publication runs on the account's browser: read its + result on `GET /items/{id}/listings`. To cross-list an item ("list it on eBay"), call this route + with that account: this API never relays a marketplace's own API. Publishing one item on several + accounts of the same marketplace risks a strike from it: name only the accounts you mean. Requires + `items:write` and an `Idempotency-Key`. Args: id (int): @@ -195,11 +197,12 @@ async def asyncio_detailed( ) -> Response[ErrorResponse | ScheduledAction]: """Publish an item on the seller's accounts - Schedules the publication of one item of the seller's stock on the given marketplace accounts, now. - The publication runs on the account's browser: read its result on `GET /items/{id}/listings`. To - cross-list an item ("list it on eBay"), call this route with that account: this API never relays a - marketplace's own API. Publishing one item on several accounts of the same marketplace risks a - strike from it: name only the accounts you mean. Requires `items:write` and an `Idempotency-Key`. + Schedules the publication of one item of the seller's stock on the given marketplace accounts, now + or at `scheduledAt` (at most 30 days ahead). The publication runs on the account's browser: read its + result on `GET /items/{id}/listings`. To cross-list an item ("list it on eBay"), call this route + with that account: this API never relays a marketplace's own API. Publishing one item on several + accounts of the same marketplace risks a strike from it: name only the accounts you mean. Requires + `items:write` and an `Idempotency-Key`. Args: id (int): @@ -234,11 +237,12 @@ async def asyncio( ) -> ErrorResponse | ScheduledAction | None: """Publish an item on the seller's accounts - Schedules the publication of one item of the seller's stock on the given marketplace accounts, now. - The publication runs on the account's browser: read its result on `GET /items/{id}/listings`. To - cross-list an item ("list it on eBay"), call this route with that account: this API never relays a - marketplace's own API. Publishing one item on several accounts of the same marketplace risks a - strike from it: name only the accounts you mean. Requires `items:write` and an `Idempotency-Key`. + Schedules the publication of one item of the seller's stock on the given marketplace accounts, now + or at `scheduledAt` (at most 30 days ahead). The publication runs on the account's browser: read its + result on `GET /items/{id}/listings`. To cross-list an item ("list it on eBay"), call this route + with that account: this API never relays a marketplace's own API. Publishing one item on several + accounts of the same marketplace risks a strike from it: name only the accounts you mean. Requires + `items:write` and an `Idempotency-Key`. Args: id (int): diff --git a/src/controlresell/generated/api/items/republish_item.py b/src/controlresell/generated/api/items/republish_item.py index 54212ec..d8d5c95 100644 --- a/src/controlresell/generated/api/items/republish_item.py +++ b/src/controlresell/generated/api/items/republish_item.py @@ -119,12 +119,12 @@ def sync_detailed( ) -> Response[ErrorResponse | ScheduledAction]: """Republish an item on the seller's accounts - Schedules the republication of one item of the seller's stock, now, exactly as the app and the MCP - tool `republish_items` do: ControlResell deletes the listing and publishes it again from the item it - holds, with the same safeguards (republication pace, publication guards). eBay, Shopify and Etsy do - not republish: their accounts are refused (`republish_not_supported`). Without `accountIds`, every - account the item is listed on. The republication runs on the account's browser: read its result on - `GET /items/{id}/listings`. + Schedules the republication of one item of the seller's stock, now or at `scheduledAt` (at most 30 + days ahead), exactly as the app and the MCP tool `republish_items` do: ControlResell deletes the + listing and publishes it again from the item it holds, with the same safeguards (republication pace, + publication guards). eBay, Shopify and Etsy do not republish: their accounts are refused + (`republish_not_supported`). Without `accountIds`, every account the item is listed on. The + republication runs on the account's browser: read its result on `GET /items/{id}/listings`. This API never relays a marketplace's own API (Vinted, eBay…): every write runs a ControlResell use case on an item of the seller's stock. To cross-list an item on another account or marketplace @@ -166,12 +166,12 @@ def sync( ) -> ErrorResponse | ScheduledAction | None: """Republish an item on the seller's accounts - Schedules the republication of one item of the seller's stock, now, exactly as the app and the MCP - tool `republish_items` do: ControlResell deletes the listing and publishes it again from the item it - holds, with the same safeguards (republication pace, publication guards). eBay, Shopify and Etsy do - not republish: their accounts are refused (`republish_not_supported`). Without `accountIds`, every - account the item is listed on. The republication runs on the account's browser: read its result on - `GET /items/{id}/listings`. + Schedules the republication of one item of the seller's stock, now or at `scheduledAt` (at most 30 + days ahead), exactly as the app and the MCP tool `republish_items` do: ControlResell deletes the + listing and publishes it again from the item it holds, with the same safeguards (republication pace, + publication guards). eBay, Shopify and Etsy do not republish: their accounts are refused + (`republish_not_supported`). Without `accountIds`, every account the item is listed on. The + republication runs on the account's browser: read its result on `GET /items/{id}/listings`. This API never relays a marketplace's own API (Vinted, eBay…): every write runs a ControlResell use case on an item of the seller's stock. To cross-list an item on another account or marketplace @@ -208,12 +208,12 @@ async def asyncio_detailed( ) -> Response[ErrorResponse | ScheduledAction]: """Republish an item on the seller's accounts - Schedules the republication of one item of the seller's stock, now, exactly as the app and the MCP - tool `republish_items` do: ControlResell deletes the listing and publishes it again from the item it - holds, with the same safeguards (republication pace, publication guards). eBay, Shopify and Etsy do - not republish: their accounts are refused (`republish_not_supported`). Without `accountIds`, every - account the item is listed on. The republication runs on the account's browser: read its result on - `GET /items/{id}/listings`. + Schedules the republication of one item of the seller's stock, now or at `scheduledAt` (at most 30 + days ahead), exactly as the app and the MCP tool `republish_items` do: ControlResell deletes the + listing and publishes it again from the item it holds, with the same safeguards (republication pace, + publication guards). eBay, Shopify and Etsy do not republish: their accounts are refused + (`republish_not_supported`). Without `accountIds`, every account the item is listed on. The + republication runs on the account's browser: read its result on `GET /items/{id}/listings`. This API never relays a marketplace's own API (Vinted, eBay…): every write runs a ControlResell use case on an item of the seller's stock. To cross-list an item on another account or marketplace @@ -253,12 +253,12 @@ async def asyncio( ) -> ErrorResponse | ScheduledAction | None: """Republish an item on the seller's accounts - Schedules the republication of one item of the seller's stock, now, exactly as the app and the MCP - tool `republish_items` do: ControlResell deletes the listing and publishes it again from the item it - holds, with the same safeguards (republication pace, publication guards). eBay, Shopify and Etsy do - not republish: their accounts are refused (`republish_not_supported`). Without `accountIds`, every - account the item is listed on. The republication runs on the account's browser: read its result on - `GET /items/{id}/listings`. + Schedules the republication of one item of the seller's stock, now or at `scheduledAt` (at most 30 + days ahead), exactly as the app and the MCP tool `republish_items` do: ControlResell deletes the + listing and publishes it again from the item it holds, with the same safeguards (republication pace, + publication guards). eBay, Shopify and Etsy do not republish: their accounts are refused + (`republish_not_supported`). Without `accountIds`, every account the item is listed on. The + republication runs on the account's browser: read its result on `GET /items/{id}/listings`. This API never relays a marketplace's own API (Vinted, eBay…): every write runs a ControlResell use case on an item of the seller's stock. To cross-list an item on another account or marketplace diff --git a/src/controlresell/generated/models/price_drop_request.py b/src/controlresell/generated/models/price_drop_request.py index 5d90268..c8bfdc4 100644 --- a/src/controlresell/generated/models/price_drop_request.py +++ b/src/controlresell/generated/models/price_drop_request.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime from collections.abc import Mapping from typing import Any, Self, TypeVar, cast from uuid import UUID @@ -21,6 +22,7 @@ class PriceDropRequest: round_type (None | str | Unset): round_step (None | str | Unset): account_ids (list[UUID] | None | Unset): + scheduled_at (datetime.datetime | None | Unset): """ type_: str @@ -28,6 +30,7 @@ class PriceDropRequest: round_type: None | str | Unset = UNSET round_step: None | str | Unset = UNSET account_ids: list[UUID] | None | Unset = UNSET + scheduled_at: datetime.datetime | None | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -59,6 +62,14 @@ def to_dict(self) -> dict[str, Any]: else: account_ids = self.account_ids + scheduled_at: None | str | Unset + if isinstance(self.scheduled_at, Unset): + scheduled_at = UNSET + elif isinstance(self.scheduled_at, datetime.datetime): + scheduled_at = self.scheduled_at.isoformat() + else: + scheduled_at = self.scheduled_at + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -73,6 +84,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["roundStep"] = round_step if account_ids is not UNSET: field_dict["accountIds"] = account_ids + if scheduled_at is not UNSET: + field_dict["scheduledAt"] = scheduled_at return field_dict @@ -123,12 +136,30 @@ def _parse_account_ids(data: object) -> list[UUID] | None | Unset: account_ids = _parse_account_ids(d.pop("accountIds", UNSET)) + def _parse_scheduled_at(data: object) -> datetime.datetime | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + scheduled_at_type_0 = datetime.datetime.fromisoformat(data) + + return scheduled_at_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(datetime.datetime | None | Unset, data) + + scheduled_at = _parse_scheduled_at(d.pop("scheduledAt", UNSET)) + price_drop_request = cls( type_=type_, value=value, round_type=round_type, round_step=round_step, account_ids=account_ids, + scheduled_at=scheduled_at, ) price_drop_request.additional_properties = d diff --git a/src/controlresell/generated/models/publish_request.py b/src/controlresell/generated/models/publish_request.py index 07ba7c6..fac8b93 100644 --- a/src/controlresell/generated/models/publish_request.py +++ b/src/controlresell/generated/models/publish_request.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime from collections.abc import Mapping from typing import Any, Self, TypeVar, cast from uuid import UUID @@ -18,10 +19,12 @@ class PublishRequest: Attributes: account_ids (list[UUID]): cancel_before_proceeding (bool | None | Unset): + scheduled_at (datetime.datetime | None | Unset): """ account_ids: list[UUID] cancel_before_proceeding: bool | None | Unset = UNSET + scheduled_at: datetime.datetime | None | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -36,6 +39,14 @@ def to_dict(self) -> dict[str, Any]: else: cancel_before_proceeding = self.cancel_before_proceeding + scheduled_at: None | str | Unset + if isinstance(self.scheduled_at, Unset): + scheduled_at = UNSET + elif isinstance(self.scheduled_at, datetime.datetime): + scheduled_at = self.scheduled_at.isoformat() + else: + scheduled_at = self.scheduled_at + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -45,6 +56,8 @@ def to_dict(self) -> dict[str, Any]: ) if cancel_before_proceeding is not UNSET: field_dict["cancelBeforeProceeding"] = cancel_before_proceeding + if scheduled_at is not UNSET: + field_dict["scheduledAt"] = scheduled_at return field_dict @@ -69,9 +82,27 @@ def _parse_cancel_before_proceeding(data: object) -> bool | None | Unset: d.pop("cancelBeforeProceeding", UNSET) ) + def _parse_scheduled_at(data: object) -> datetime.datetime | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + scheduled_at_type_0 = datetime.datetime.fromisoformat(data) + + return scheduled_at_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(datetime.datetime | None | Unset, data) + + scheduled_at = _parse_scheduled_at(d.pop("scheduledAt", UNSET)) + publish_request = cls( account_ids=account_ids, cancel_before_proceeding=cancel_before_proceeding, + scheduled_at=scheduled_at, ) publish_request.additional_properties = d diff --git a/src/controlresell/generated/models/republish_request.py b/src/controlresell/generated/models/republish_request.py index fe0f530..e43d510 100644 --- a/src/controlresell/generated/models/republish_request.py +++ b/src/controlresell/generated/models/republish_request.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime from collections.abc import Mapping from typing import TYPE_CHECKING, Any, Self, TypeVar, cast from uuid import UUID @@ -24,12 +25,14 @@ class RepublishRequest: price_drop (None | PriceDropRequest | Unset): price_variation (None | str | Unset): allow_if_deleted (bool | None | Unset): + scheduled_at (datetime.datetime | None | Unset): """ account_ids: list[UUID] | None | Unset = UNSET price_drop: None | PriceDropRequest | Unset = UNSET price_variation: None | str | Unset = UNSET allow_if_deleted: bool | None | Unset = UNSET + scheduled_at: datetime.datetime | None | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -67,6 +70,14 @@ def to_dict(self) -> dict[str, Any]: else: allow_if_deleted = self.allow_if_deleted + scheduled_at: None | str | Unset + if isinstance(self.scheduled_at, Unset): + scheduled_at = UNSET + elif isinstance(self.scheduled_at, datetime.datetime): + scheduled_at = self.scheduled_at.isoformat() + else: + scheduled_at = self.scheduled_at + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) @@ -78,6 +89,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["priceVariation"] = price_variation if allow_if_deleted is not UNSET: field_dict["allowIfDeleted"] = allow_if_deleted + if scheduled_at is not UNSET: + field_dict["scheduledAt"] = scheduled_at return field_dict @@ -144,11 +157,29 @@ def _parse_allow_if_deleted(data: object) -> bool | None | Unset: allow_if_deleted = _parse_allow_if_deleted(d.pop("allowIfDeleted", UNSET)) + def _parse_scheduled_at(data: object) -> datetime.datetime | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + scheduled_at_type_0 = datetime.datetime.fromisoformat(data) + + return scheduled_at_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(datetime.datetime | None | Unset, data) + + scheduled_at = _parse_scheduled_at(d.pop("scheduledAt", UNSET)) + republish_request = cls( account_ids=account_ids, price_drop=price_drop, price_variation=price_variation, allow_if_deleted=allow_if_deleted, + scheduled_at=scheduled_at, ) republish_request.additional_properties = d diff --git a/tests/test_client.py b/tests/test_client.py index b04da88..d9c70f2 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -222,6 +222,21 @@ def test_price_drop_posts_to_the_price_drop_route(): assert json.loads(requests[0].content) == {"type": "PERCENT", "value": "10"} +def test_publish_price_drop_and_republish_send_scheduled_at(): + import datetime + + from controlresell.generated.models import PriceDropRequest, PublishRequest, RepublishRequest + + at = datetime.datetime(2026, 11, 2, 10, 0, tzinfo=datetime.timezone.utc) + requests, sdk = client(lambda r: reply(ACTION, 202)) + sdk.items.publish(7, PublishRequest(account_ids=[ACCOUNT], scheduled_at=at)) + sdk.items.price_drop(7, PriceDropRequest(type_="PERCENT", value="10", scheduled_at=at)) + sdk.items.republish(7, RepublishRequest(scheduled_at=at)) + assert len(requests) == 3 + for request in requests: + assert json.loads(request.content)["scheduledAt"] == "2026-11-02T10:00:00+00:00" + + def test_delete_resolves_on_204_and_raises_a_typed_error_on_409(): requests, sdk = client(lambda r: httpx.Response(204)) assert sdk.items.delete(7) is None