python-bestbuy 0.2.1__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/PKG-INFO +1 -1
- python_bestbuy-0.2.3/bestbuy/exceptions/__init__.py +123 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/models/__init__.py +4 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/models/commerce.py +97 -21
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/utils/errors.py +19 -3
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/pyproject.toml +1 -1
- python_bestbuy-0.2.1/bestbuy/exceptions.py +0 -59
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/.gitignore +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/LICENSE +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/README.md +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/__init__.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/clients/__init__.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/clients/base.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/clients/catalog.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/clients/commerce.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/configs/__init__.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/configs/base.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/configs/catalog.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/configs/commerce.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/loggers.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/models/catalog.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/operations/__init__.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/operations/base.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/operations/catalog.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/operations/commerce.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/operations/pagination.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/py.typed +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/query.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/typing.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/utils/__init__.py +0 -0
- {python_bestbuy-0.2.1 → python_bestbuy-0.2.3}/bestbuy/utils/encryption.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-bestbuy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Python client library for Best Buy's Catalog and Commerce APIs
|
|
5
5
|
Project-URL: Homepage, https://github.com/bbify/python-bestbuy
|
|
6
6
|
Project-URL: Repository, https://github.com/bbify/python-bestbuy
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Custom exceptions for the Best Buy API client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BestBuyError(Exception):
|
|
9
|
+
"""Base exception for all Best Buy API errors."""
|
|
10
|
+
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigError(BestBuyError):
|
|
15
|
+
"""Raised when there's an error with client configuration."""
|
|
16
|
+
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AuthenticationError(BestBuyError):
|
|
21
|
+
"""Raised when authentication fails."""
|
|
22
|
+
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SessionRequiredError(BestBuyError):
|
|
27
|
+
"""Raised when an operation requires an active session but none exists."""
|
|
28
|
+
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class APIError(BestBuyError):
|
|
33
|
+
"""Raised when the API returns an error.
|
|
34
|
+
|
|
35
|
+
Used to handle errors from both Commerce and Catalog APIs.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
message: Human-readable error message
|
|
39
|
+
code: Error code from the API (if available)
|
|
40
|
+
sku: SKU that caused the error (if available)
|
|
41
|
+
response_text: Raw response text (XML or JSON)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
message: str,
|
|
47
|
+
code: str | None = None,
|
|
48
|
+
sku: str | None = None,
|
|
49
|
+
response_text: str | None = None,
|
|
50
|
+
):
|
|
51
|
+
self.message = message
|
|
52
|
+
self.code = code
|
|
53
|
+
self.sku = sku
|
|
54
|
+
self.response_text = response_text
|
|
55
|
+
|
|
56
|
+
# Build error message
|
|
57
|
+
parts = [message]
|
|
58
|
+
if code:
|
|
59
|
+
parts.append(f"Error code: {code}")
|
|
60
|
+
if sku:
|
|
61
|
+
parts.append(f"SKU: {sku}")
|
|
62
|
+
|
|
63
|
+
super().__init__(" | ".join(parts))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# Commerce-specific exceptions
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class OrderErrorMessage:
|
|
71
|
+
"""A single message from a commerce order error response.
|
|
72
|
+
|
|
73
|
+
Attributes:
|
|
74
|
+
code: The error code (from the ``item-id`` XML attribute),
|
|
75
|
+
e.g. ``"INVALID_CID"``, ``"MISSINGPARTNERID"``.
|
|
76
|
+
text: The human-readable error message.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
code: str
|
|
80
|
+
text: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CommerceOrderError(APIError):
|
|
84
|
+
"""Raised when the Commerce API rejects an order (HTTP 400).
|
|
85
|
+
|
|
86
|
+
Contains structured error messages from the
|
|
87
|
+
``<order-response><messages>`` element. Multiple messages may be
|
|
88
|
+
present when several validation errors occur simultaneously.
|
|
89
|
+
|
|
90
|
+
Inherits from :class:`APIError` so existing ``except APIError``
|
|
91
|
+
catch blocks still work. Callers can also catch
|
|
92
|
+
``CommerceOrderError`` specifically for structured access to the
|
|
93
|
+
error messages list.
|
|
94
|
+
|
|
95
|
+
Attributes:
|
|
96
|
+
messages: List of structured error messages with codes.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
messages: list[OrderErrorMessage],
|
|
102
|
+
response_text: str | None = None,
|
|
103
|
+
) -> None:
|
|
104
|
+
self.messages = messages
|
|
105
|
+
primary = (
|
|
106
|
+
messages[0]
|
|
107
|
+
if messages
|
|
108
|
+
else OrderErrorMessage(code="UNKNOWN", text="Unknown order error")
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
parts = [primary.text]
|
|
112
|
+
if primary.code:
|
|
113
|
+
parts.append(f"Error code: {primary.code}")
|
|
114
|
+
if len(messages) > 1:
|
|
115
|
+
parts.append(f"{len(messages)} total errors")
|
|
116
|
+
|
|
117
|
+
# Initialize APIError fields directly (skip APIError.__init__
|
|
118
|
+
# to control the str representation)
|
|
119
|
+
self.message = primary.text
|
|
120
|
+
self.code = primary.code
|
|
121
|
+
self.sku = None
|
|
122
|
+
self.response_text = response_text
|
|
123
|
+
BestBuyError.__init__(self, " | ".join(parts))
|
|
@@ -88,9 +88,11 @@ from .commerce import (
|
|
|
88
88
|
OrderList,
|
|
89
89
|
OrderQueryRequest,
|
|
90
90
|
OrderResponse,
|
|
91
|
+
OrderResponseMessage,
|
|
91
92
|
OrderSubmitGuestRequest,
|
|
92
93
|
OrderSubmitRegisteredRequest,
|
|
93
94
|
OrderSubmitResponse,
|
|
95
|
+
OrderItemStatus,
|
|
94
96
|
OrderStatus,
|
|
95
97
|
PriceQueryRequest,
|
|
96
98
|
PriceResponse,
|
|
@@ -201,9 +203,11 @@ __all__ = [
|
|
|
201
203
|
"OrderList",
|
|
202
204
|
"OrderQueryRequest",
|
|
203
205
|
"OrderResponse",
|
|
206
|
+
"OrderResponseMessage",
|
|
204
207
|
"OrderSubmitGuestRequest",
|
|
205
208
|
"OrderSubmitRegisteredRequest",
|
|
206
209
|
"OrderSubmitResponse",
|
|
210
|
+
"OrderItemStatus",
|
|
207
211
|
"OrderStatus",
|
|
208
212
|
"PriceQueryRequest",
|
|
209
213
|
"PriceResponse",
|
|
@@ -7,32 +7,75 @@ from pydantic_xml import BaseXmlModel, element, attr, wrapped
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class OrderStatus(str, Enum):
|
|
10
|
+
"""Overall order status values returned by the Commerce API."""
|
|
11
|
+
|
|
12
|
+
CLOSED = "CLOSED"
|
|
13
|
+
INCOMPLETE = "INCOMPLETE"
|
|
14
|
+
PAYMENT_COLLECTED = "PAYMENT_COLLECTED"
|
|
15
|
+
RECEIVED = "RECEIVED"
|
|
16
|
+
SHIPPED = "SHIPPED"
|
|
17
|
+
SHIPPING = "SHIPPING"
|
|
18
|
+
SUBMITTED = "SUBMITTED"
|
|
19
|
+
WAITING_ACCEPTANCE = "WAITING_ACCEPTANCE"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OrderItemStatus(str, Enum):
|
|
23
|
+
"""Line-item status values for individual items in an order."""
|
|
24
|
+
|
|
25
|
+
# Item is waiting to be picked up for return.
|
|
10
26
|
AWAITING_RETURN_PICKUP = "Awaiting Return Pickup"
|
|
27
|
+
# Your appointment has not been set
|
|
11
28
|
AWAITING_SCHEDULING = "Awaiting Scheduling"
|
|
29
|
+
# Item is currently backordered and cannot be fulfilled until more
|
|
30
|
+
# inventory becomes available
|
|
12
31
|
BACKORDERED = "Backordered"
|
|
32
|
+
# Your item was canceled
|
|
13
33
|
CANCELED = "Canceled"
|
|
34
|
+
# The process of order through fulfillment is completed/delivered
|
|
35
|
+
# to the end user
|
|
14
36
|
COMPLETED = "Completed"
|
|
37
|
+
# Carrier has indicated that your shipment may have been delayed
|
|
15
38
|
DELAYED = "Delayed"
|
|
39
|
+
# The process of order through fulfillment is completed/delivered
|
|
40
|
+
# to the end user
|
|
16
41
|
DELIVERED = "Delivered"
|
|
42
|
+
# Order is in progress — still going through statuses before delivery
|
|
17
43
|
IN_PROGRESS = "In Progress"
|
|
18
|
-
|
|
44
|
+
# The item has been returned
|
|
19
45
|
ITEM_RETURNED = "Item Returned"
|
|
46
|
+
# Your order has been received
|
|
20
47
|
ORDER_RECEIVED = "Order Received"
|
|
48
|
+
# The item is out for delivery
|
|
21
49
|
OUT_FOR_DELIVERY = "Out for Delivery"
|
|
50
|
+
# The item is out of stock and awaiting more inventory
|
|
22
51
|
OUT_OF_STOCK = "Out of Stock"
|
|
52
|
+
# Pending cancellation (rarely seen)
|
|
23
53
|
PENDING_CANCELLATION = "Pending Cancellation"
|
|
54
|
+
# The item was picked up (usually store pickup fulfillment)
|
|
24
55
|
PICKED_UP = "Picked Up"
|
|
56
|
+
# Item is on pre-order and not yet available for shipping
|
|
25
57
|
PRE_ORDERED = "Pre-Ordered"
|
|
58
|
+
# Getting the item ready for shipment or delivery
|
|
26
59
|
PREPARING = "Preparing"
|
|
60
|
+
# The item is ready to be picked up at the store
|
|
27
61
|
READY_TO_PICK_UP = "Ready to Pick Up"
|
|
62
|
+
# Item is pending a refund
|
|
28
63
|
REFUND_PENDING = "Refund Pending"
|
|
64
|
+
# Your appointment needs to be rescheduled
|
|
29
65
|
RESCHEDULE_REQUIRED = "Reschedule Required"
|
|
66
|
+
# The returning item is in transit to the return warehouse
|
|
30
67
|
RETURN_IN_TRANSIT = "Return In Transit"
|
|
68
|
+
# The item has been received by the return warehouse and is in review
|
|
31
69
|
RETURN_RECEIVED = "Return Received"
|
|
70
|
+
# Confirmation of a return request
|
|
32
71
|
RETURN_REQUEST_CONFIRMED = "Return Request Confirmed"
|
|
72
|
+
# Item has been returned
|
|
33
73
|
RETURNED = "Returned"
|
|
74
|
+
# Item is scheduled for fulfillment
|
|
34
75
|
SCHEDULED = "Scheduled"
|
|
76
|
+
# Item needs to be scheduled
|
|
35
77
|
SCHEDULING_NEEDED = "Scheduling Needed"
|
|
78
|
+
# Item has shipped
|
|
36
79
|
SHIPPED = "Shipped"
|
|
37
80
|
|
|
38
81
|
|
|
@@ -80,13 +123,17 @@ class AvailabilityQueryRequest(BaseXmlModel, tag="availability-query"):
|
|
|
80
123
|
|
|
81
124
|
|
|
82
125
|
class AvailabilityQueryResponse(BaseXmlModel, tag="availability-query"):
|
|
83
|
-
sku_id: str = attr(name="sku-id")
|
|
84
|
-
order_code: int = element(tag="order-code")
|
|
85
|
-
display_message: str = element(tag="display-message")
|
|
86
|
-
home_delivery_code: int = element(tag="home-delivery-code")
|
|
87
|
-
home_delivery_message: str = element(
|
|
88
|
-
|
|
89
|
-
|
|
126
|
+
sku_id: Optional[str] = attr(name="sku-id", default=None)
|
|
127
|
+
order_code: Optional[int] = element(tag="order-code", default=None)
|
|
128
|
+
display_message: Optional[str] = element(tag="display-message", default=None)
|
|
129
|
+
home_delivery_code: Optional[int] = element(tag="home-delivery-code", default=None)
|
|
130
|
+
home_delivery_message: Optional[str] = element(
|
|
131
|
+
tag="home-delivery-message", default=None
|
|
132
|
+
)
|
|
133
|
+
instore_availability: Optional[bool] = element(
|
|
134
|
+
tag="instore-availability", default=None
|
|
135
|
+
)
|
|
136
|
+
max_quantity: Optional[int] = element(tag="max-quantity", default=None)
|
|
90
137
|
available_for_shipping: Optional[bool] = element(
|
|
91
138
|
tag="available-for-shipping", default=None
|
|
92
139
|
)
|
|
@@ -125,8 +172,8 @@ class ShippingOption(BaseXmlModel, tag="option"):
|
|
|
125
172
|
|
|
126
173
|
|
|
127
174
|
class ShippingOptionsResponse(BaseXmlModel, tag="shipping-options"):
|
|
128
|
-
sku_id: str = attr(name="sku-id")
|
|
129
|
-
free_shipping: bool = attr(name="free-shipping")
|
|
175
|
+
sku_id: Optional[str] = attr(name="sku-id", default=None)
|
|
176
|
+
free_shipping: Optional[bool] = attr(name="free-shipping", default=None)
|
|
130
177
|
options: List[ShippingOption] = element(tag="option", default_factory=list)
|
|
131
178
|
|
|
132
179
|
|
|
@@ -188,13 +235,17 @@ class ProductServiceRequest(BaseXmlModel, tag="productservice-request"):
|
|
|
188
235
|
|
|
189
236
|
|
|
190
237
|
class AvailabilityQuery(BaseXmlModel, tag="availability-query"):
|
|
191
|
-
sku_id: str = attr(name="sku-id")
|
|
192
|
-
order_code: int = element(tag="order-code")
|
|
193
|
-
display_message: str = element(tag="display-message")
|
|
194
|
-
home_delivery_code: int = element(tag="home-delivery-code")
|
|
195
|
-
home_delivery_message: str = element(
|
|
196
|
-
|
|
197
|
-
|
|
238
|
+
sku_id: Optional[str] = attr(name="sku-id", default=None)
|
|
239
|
+
order_code: Optional[int] = element(tag="order-code", default=None)
|
|
240
|
+
display_message: Optional[str] = element(tag="display-message", default=None)
|
|
241
|
+
home_delivery_code: Optional[int] = element(tag="home-delivery-code", default=None)
|
|
242
|
+
home_delivery_message: Optional[str] = element(
|
|
243
|
+
tag="home-delivery-message", default=None
|
|
244
|
+
)
|
|
245
|
+
instore_availability: Optional[bool] = element(
|
|
246
|
+
tag="instore-availability", default=None
|
|
247
|
+
)
|
|
248
|
+
max_quantity: Optional[int] = element(tag="max-quantity", default=None)
|
|
198
249
|
|
|
199
250
|
|
|
200
251
|
class ProductServiceResponse(BaseXmlModel, tag="productservice-response"):
|
|
@@ -236,6 +287,14 @@ class OrderItem(BaseXmlModel, tag="item"):
|
|
|
236
287
|
link: Optional[Link] = element(tag="link", default=None)
|
|
237
288
|
sku: Optional[str] = attr(name="sku", default=None)
|
|
238
289
|
backordered: Optional[bool] = attr(name="backordered", default=None)
|
|
290
|
+
cancellable: Optional[bool] = attr(name="cancellable", default=None)
|
|
291
|
+
parent_item: Optional[str] = attr(name="parent-item", default=None)
|
|
292
|
+
type: Optional[str] = attr(name="type", default=None)
|
|
293
|
+
line_status: Optional[OrderItemStatus] = attr(name="line-status", default=None)
|
|
294
|
+
line_status_message: Optional[str] = attr(name="line-status-message", default=None)
|
|
295
|
+
expected_delivery_date: Optional[str] = attr(
|
|
296
|
+
name="expected-delivery-date", default=None
|
|
297
|
+
)
|
|
239
298
|
description: Optional[str] = element(tag="description", default=None)
|
|
240
299
|
unit_price: Optional[UnitPrice] = element(tag="unit-price", default=None)
|
|
241
300
|
cost: Optional[Cost] = element(tag="cost", default=None)
|
|
@@ -274,13 +333,17 @@ class Shipping(BaseXmlModel, tag="shipping"):
|
|
|
274
333
|
|
|
275
334
|
class AddressFulfillment(BaseXmlModel, tag="address-fulfillment"):
|
|
276
335
|
list: Optional[str] = attr(name="list", default=None)
|
|
277
|
-
item_id: str = attr(name="item-id")
|
|
336
|
+
item_id: Optional[str] = attr(name="item-id", default=None)
|
|
278
337
|
shipping: Optional[Shipping] = element(tag="shipping", default=None)
|
|
279
338
|
address: Optional[OrderAddress] = element(tag="address", default=None)
|
|
280
339
|
id: Optional[str] = attr(name="id", default=None)
|
|
281
340
|
estimated_shipping_date: Optional[date] = attr(
|
|
282
341
|
name="estimated-shipping-date", default=None
|
|
283
342
|
)
|
|
343
|
+
shipping_option: Optional[str] = attr(name="shipping-option", default=None)
|
|
344
|
+
shipment_date: Optional[str] = attr(name="shipment-date", default=None)
|
|
345
|
+
shipment_carrier: Optional[str] = attr(name="shipment-carrier", default=None)
|
|
346
|
+
tracking_number: Optional[str] = attr(name="tracking-number", default=None)
|
|
284
347
|
|
|
285
348
|
|
|
286
349
|
class FriendsFamilyDetails(BaseXmlModel, tag="friends-family-details"):
|
|
@@ -310,6 +373,7 @@ class HomeDeliveryFulfillment(BaseXmlModel, tag="homedelivery-fulfillment"):
|
|
|
310
373
|
|
|
311
374
|
|
|
312
375
|
class Fulfillment(BaseXmlModel, tag="fulfillment"):
|
|
376
|
+
status: Optional[str] = attr(name="status", default=None)
|
|
313
377
|
address_fulfillment: Optional[AddressFulfillment] = element(
|
|
314
378
|
tag="address-fulfillment", default=None
|
|
315
379
|
)
|
|
@@ -336,6 +400,7 @@ class CCTender(BaseXmlModel, tag="cc-tender"):
|
|
|
336
400
|
|
|
337
401
|
cid: Optional[str] = attr(name="cid", default=None)
|
|
338
402
|
last_four: Optional[str] = attr(name="last-four", default=None)
|
|
403
|
+
list: Optional[str] = attr(name="list", default=None)
|
|
339
404
|
name: Optional[str] = attr(name="name", default=None)
|
|
340
405
|
id: Optional[str] = attr(name="id", default=None)
|
|
341
406
|
exp_date: Optional[str] = attr(name="exp-date", default=None)
|
|
@@ -516,6 +581,7 @@ class OrderSubmitRegisteredRequest(BaseXmlModel, tag="order"):
|
|
|
516
581
|
|
|
517
582
|
id: str = attr(name="id")
|
|
518
583
|
partner_id: Optional[str] = attr(name="partner-id", default=None)
|
|
584
|
+
tec_code: Optional[str] = attr(name="tec-code", default=None)
|
|
519
585
|
reviewable: bool = attr(name="reviewable", default=False)
|
|
520
586
|
order_list: OrderList = element(tag="list")
|
|
521
587
|
fulfillment: Fulfillment = element(tag="fulfillment")
|
|
@@ -531,17 +597,18 @@ class OrderSubmitGuestRequest(BaseXmlModel, tag="order"):
|
|
|
531
597
|
|
|
532
598
|
id: str = attr(name="id")
|
|
533
599
|
partner_id: Optional[str] = attr(name="partner-id", default=None)
|
|
600
|
+
tec_code: Optional[str] = attr(name="tec-code", default=None)
|
|
534
601
|
reviewable: bool = attr(name="reviewable", default=False)
|
|
535
602
|
order_list: OrderList = element(tag="list")
|
|
536
603
|
fulfillment: Fulfillment = element(tag="fulfillment")
|
|
537
604
|
tender: GuestTender = element(tag="tender")
|
|
538
605
|
|
|
539
606
|
|
|
540
|
-
class OrderResponse(BaseXmlModel, tag="order"):
|
|
607
|
+
class OrderResponse(BaseXmlModel, tag="order", search_mode="unordered"):
|
|
541
608
|
id: Optional[str] = attr(name="id", default=None)
|
|
542
609
|
reviewable: Optional[bool] = attr(name="reviewable", default=None)
|
|
543
610
|
partner_id: Optional[str] = attr(name="partner-id", default=None)
|
|
544
|
-
status: Optional[
|
|
611
|
+
status: Optional[str] = attr(name="status", default=None)
|
|
545
612
|
order_date: Optional[date] = attr(name="order-date", default=None)
|
|
546
613
|
total: Optional[Decimal] = attr(name="total", default=None)
|
|
547
614
|
given_id: Optional[str] = attr(name="given-id", default=None)
|
|
@@ -560,6 +627,13 @@ class IdMapEntry(BaseXmlModel, tag="entry"):
|
|
|
560
627
|
value: str = element(tag="value")
|
|
561
628
|
|
|
562
629
|
|
|
630
|
+
class OrderResponseMessage(BaseXmlModel, tag="message"):
|
|
631
|
+
"""A message from an order response (warning/info on success, error on 400)."""
|
|
632
|
+
|
|
633
|
+
item_id: Optional[str] = attr(name="item-id", default=None)
|
|
634
|
+
text: Optional[str] = None
|
|
635
|
+
|
|
636
|
+
|
|
563
637
|
class OrderSubmitResponse(BaseXmlModel, tag="order-response"):
|
|
564
638
|
version: str = attr(name="version")
|
|
565
639
|
status: str = attr(name="status")
|
|
@@ -567,7 +641,9 @@ class OrderSubmitResponse(BaseXmlModel, tag="order-response"):
|
|
|
567
641
|
id_map: Optional[List[IdMapEntry]] = wrapped(
|
|
568
642
|
"id-map", element(tag="entry", default_factory=list)
|
|
569
643
|
)
|
|
570
|
-
messages:
|
|
644
|
+
messages: List[OrderResponseMessage] = wrapped(
|
|
645
|
+
"messages", element(tag="message", default_factory=list)
|
|
646
|
+
)
|
|
571
647
|
|
|
572
648
|
|
|
573
649
|
class PublicKeyEncryptionResponse(BaseXmlModel, tag="publicKeyEncryption"):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from xml.etree import ElementTree as ET
|
|
3
3
|
|
|
4
|
-
from ..exceptions import APIError
|
|
4
|
+
from ..exceptions import APIError, CommerceOrderError, OrderErrorMessage
|
|
5
5
|
from ..models import ApiError, ApiErrors, SimpleError
|
|
6
6
|
|
|
7
7
|
|
|
@@ -80,19 +80,35 @@ def check_for_xml_errors(response_text: str | None) -> None:
|
|
|
80
80
|
except Exception:
|
|
81
81
|
pass
|
|
82
82
|
|
|
83
|
+
# Check for order-response messages format:
|
|
84
|
+
# <order-response><messages><message item-id="CODE">text</message>
|
|
85
|
+
elif root.tag == "order-response":
|
|
86
|
+
messages_elem = root.find("messages")
|
|
87
|
+
if messages_elem is not None:
|
|
88
|
+
order_messages = []
|
|
89
|
+
for msg_elem in messages_elem.findall("message"):
|
|
90
|
+
code = msg_elem.get("item-id", "UNKNOWN")
|
|
91
|
+
text = msg_elem.text or ""
|
|
92
|
+
order_messages.append(OrderErrorMessage(code=code, text=text))
|
|
93
|
+
if order_messages:
|
|
94
|
+
raise CommerceOrderError(
|
|
95
|
+
messages=order_messages,
|
|
96
|
+
response_text=response_text,
|
|
97
|
+
)
|
|
98
|
+
|
|
83
99
|
# Check for errors embedded in other response types
|
|
84
100
|
else:
|
|
85
101
|
# Look for <error> child elements
|
|
86
102
|
error_elem = root.find(".//error")
|
|
87
103
|
if error_elem is not None:
|
|
88
|
-
|
|
104
|
+
error_code = error_elem.get("code")
|
|
89
105
|
message_elem = error_elem.find("message")
|
|
90
106
|
if message_elem is not None:
|
|
91
107
|
message = message_elem.text or "Unknown error"
|
|
92
108
|
sku = message_elem.get("sku")
|
|
93
109
|
raise APIError(
|
|
94
110
|
message=message,
|
|
95
|
-
code=
|
|
111
|
+
code=error_code,
|
|
96
112
|
sku=sku,
|
|
97
113
|
response_text=response_text,
|
|
98
114
|
)
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""Custom exceptions for the Best Buy API client."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class BestBuyError(Exception):
|
|
5
|
-
"""Base exception for all Best Buy API errors."""
|
|
6
|
-
|
|
7
|
-
pass
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ConfigError(BestBuyError):
|
|
11
|
-
"""Raised when there's an error with client configuration."""
|
|
12
|
-
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class AuthenticationError(BestBuyError):
|
|
17
|
-
"""Raised when authentication fails."""
|
|
18
|
-
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class SessionRequiredError(BestBuyError):
|
|
23
|
-
"""Raised when an operation requires an active session but none exists."""
|
|
24
|
-
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class APIError(BestBuyError):
|
|
29
|
-
"""Raised when the API returns an error.
|
|
30
|
-
|
|
31
|
-
Used to handle errors from both Commerce and Catalog APIs.
|
|
32
|
-
|
|
33
|
-
Attributes:
|
|
34
|
-
message: Human-readable error message
|
|
35
|
-
code: Error code from the API (if available)
|
|
36
|
-
sku: SKU that caused the error (if available)
|
|
37
|
-
response_text: Raw response text (XML or JSON)
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
message: str,
|
|
43
|
-
code: str | None = None,
|
|
44
|
-
sku: str | None = None,
|
|
45
|
-
response_text: str | None = None,
|
|
46
|
-
):
|
|
47
|
-
self.message = message
|
|
48
|
-
self.code = code
|
|
49
|
-
self.sku = sku
|
|
50
|
-
self.response_text = response_text
|
|
51
|
-
|
|
52
|
-
# Build error message
|
|
53
|
-
parts = [message]
|
|
54
|
-
if code:
|
|
55
|
-
parts.append(f"Error code: {code}")
|
|
56
|
-
if sku:
|
|
57
|
-
parts.append(f"SKU: {sku}")
|
|
58
|
-
|
|
59
|
-
super().__init__(" | ".join(parts))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|