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