shipthisapi-python 2.1.0__tar.gz → 3.0.0__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.
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/PKG-INFO +4 -2
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/ShipthisAPI/__init__.py +1 -1
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/ShipthisAPI/shipthisapi.py +165 -112
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/setup.py +6 -3
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/shipthisapi_python.egg-info/PKG-INFO +4 -2
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/shipthisapi_python.egg-info/SOURCES.txt +1 -0
- shipthisapi_python-3.0.0/shipthisapi_python.egg-info/requires.txt +1 -0
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/README.md +0 -0
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/setup.cfg +0 -0
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/shipthisapi_python.egg-info/dependency_links.txt +0 -0
- {shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/shipthisapi_python.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shipthisapi-python
|
|
3
|
-
Version:
|
|
4
|
-
Summary: ShipthisAPI utility package
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: ShipthisAPI async utility package
|
|
5
5
|
Home-page: https://github.com/shipthisco/shipthisapi-python
|
|
6
6
|
Author: Mayur Rawte
|
|
7
7
|
Author-email: mayur@shipthis.co
|
|
@@ -9,12 +9,14 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: httpx>=0.24.0
|
|
12
13
|
Dynamic: author
|
|
13
14
|
Dynamic: author-email
|
|
14
15
|
Dynamic: classifier
|
|
15
16
|
Dynamic: description
|
|
16
17
|
Dynamic: description-content-type
|
|
17
18
|
Dynamic: home-page
|
|
19
|
+
Dynamic: requires-dist
|
|
18
20
|
Dynamic: summary
|
|
19
21
|
|
|
20
22
|
# ShipthisAPI Python
|
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
"""Shipthis API Client.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An async Python client for the Shipthis public API.
|
|
4
4
|
|
|
5
5
|
Usage:
|
|
6
|
+
import asyncio
|
|
6
7
|
from ShipthisAPI import ShipthisAPI
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
async def main():
|
|
10
|
+
# Initialize the client
|
|
11
|
+
client = ShipthisAPI(
|
|
12
|
+
organisation="your_org_id",
|
|
13
|
+
x_api_key="your_api_key",
|
|
14
|
+
region_id="your_region",
|
|
15
|
+
location_id="your_location"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Connect and validate
|
|
19
|
+
await client.connect()
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
# Get items from a collection
|
|
22
|
+
items = await client.get_list("shipment")
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
# Patch document fields
|
|
25
|
+
await client.patch_item("fcl_load", doc_id, {"status": "completed"})
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
item = client.get_one_item("shipment", doc_id="abc123")
|
|
27
|
+
asyncio.run(main())
|
|
23
28
|
"""
|
|
24
29
|
|
|
25
|
-
from typing import Any, Dict, List, Optional
|
|
30
|
+
from typing import Any, Dict, List, Optional
|
|
26
31
|
import json
|
|
27
|
-
import
|
|
32
|
+
import httpx
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
class ShipthisAPIError(Exception):
|
|
@@ -50,7 +55,7 @@ class ShipthisRequestError(ShipthisAPIError):
|
|
|
50
55
|
|
|
51
56
|
|
|
52
57
|
class ShipthisAPI:
|
|
53
|
-
"""Shipthis API client for public API access.
|
|
58
|
+
"""Async Shipthis API client for public API access.
|
|
54
59
|
|
|
55
60
|
Attributes:
|
|
56
61
|
base_api_endpoint: The base URL for the API.
|
|
@@ -60,6 +65,7 @@ class ShipthisAPI:
|
|
|
60
65
|
region_id: Region ID for requests.
|
|
61
66
|
location_id: Location ID for requests.
|
|
62
67
|
timeout: Request timeout in seconds.
|
|
68
|
+
custom_headers: Custom headers to override defaults.
|
|
63
69
|
"""
|
|
64
70
|
|
|
65
71
|
DEFAULT_TIMEOUT = 30
|
|
@@ -68,28 +74,28 @@ class ShipthisAPI:
|
|
|
68
74
|
def __init__(
|
|
69
75
|
self,
|
|
70
76
|
organisation: str,
|
|
71
|
-
x_api_key: str,
|
|
77
|
+
x_api_key: str = None,
|
|
72
78
|
user_type: str = "employee",
|
|
73
79
|
region_id: str = None,
|
|
74
80
|
location_id: str = None,
|
|
75
81
|
timeout: int = None,
|
|
76
82
|
base_url: str = None,
|
|
83
|
+
custom_headers: Dict[str, str] = None,
|
|
77
84
|
) -> None:
|
|
78
85
|
"""Initialize the Shipthis API client.
|
|
79
86
|
|
|
80
87
|
Args:
|
|
81
88
|
organisation: Your organisation ID.
|
|
82
|
-
x_api_key: Your API key.
|
|
89
|
+
x_api_key: Your API key (optional if using custom_headers with auth).
|
|
83
90
|
user_type: User type for requests (default: "employee").
|
|
84
91
|
region_id: Region ID for requests.
|
|
85
92
|
location_id: Location ID for requests.
|
|
86
93
|
timeout: Request timeout in seconds (default: 30).
|
|
87
94
|
base_url: Custom base URL (optional, for testing).
|
|
95
|
+
custom_headers: Custom headers that override defaults.
|
|
88
96
|
"""
|
|
89
97
|
if not organisation:
|
|
90
98
|
raise ValueError("organisation is required")
|
|
91
|
-
if not x_api_key:
|
|
92
|
-
raise ValueError("x_api_key is required")
|
|
93
99
|
|
|
94
100
|
self.x_api_key = x_api_key
|
|
95
101
|
self.organisation_id = organisation
|
|
@@ -98,6 +104,7 @@ class ShipthisAPI:
|
|
|
98
104
|
self.location_id = location_id
|
|
99
105
|
self.timeout = timeout or self.DEFAULT_TIMEOUT
|
|
100
106
|
self.base_api_endpoint = base_url or self.BASE_API_ENDPOINT
|
|
107
|
+
self.custom_headers = custom_headers or {}
|
|
101
108
|
self.organisation_info = None
|
|
102
109
|
self.is_connected = False
|
|
103
110
|
|
|
@@ -111,39 +118,50 @@ class ShipthisAPI:
|
|
|
111
118
|
self.region_id = region_id
|
|
112
119
|
self.location_id = location_id
|
|
113
120
|
|
|
114
|
-
def _get_headers(self) -> Dict[str, str]:
|
|
121
|
+
def _get_headers(self, override_headers: Dict[str, str] = None) -> Dict[str, str]:
|
|
115
122
|
"""Build request headers.
|
|
116
123
|
|
|
124
|
+
Args:
|
|
125
|
+
override_headers: Headers to override for this specific request.
|
|
126
|
+
|
|
117
127
|
Returns:
|
|
118
128
|
Dictionary of headers.
|
|
119
129
|
"""
|
|
120
130
|
headers = {
|
|
121
|
-
"x-api-key": self.x_api_key,
|
|
122
131
|
"organisation": self.organisation_id,
|
|
123
132
|
"usertype": self.user_type,
|
|
124
133
|
"Content-Type": "application/json",
|
|
125
134
|
"Accept": "application/json",
|
|
126
135
|
}
|
|
136
|
+
if self.x_api_key:
|
|
137
|
+
headers["x-api-key"] = self.x_api_key
|
|
127
138
|
if self.region_id:
|
|
128
139
|
headers["region"] = self.region_id
|
|
129
140
|
if self.location_id:
|
|
130
141
|
headers["location"] = self.location_id
|
|
142
|
+
# Apply custom headers from init
|
|
143
|
+
headers.update(self.custom_headers)
|
|
144
|
+
# Apply per-request override headers
|
|
145
|
+
if override_headers:
|
|
146
|
+
headers.update(override_headers)
|
|
131
147
|
return headers
|
|
132
148
|
|
|
133
|
-
def _make_request(
|
|
149
|
+
async def _make_request(
|
|
134
150
|
self,
|
|
135
151
|
method: str,
|
|
136
152
|
path: str,
|
|
137
153
|
query_params: Dict[str, Any] = None,
|
|
138
154
|
request_data: Dict[str, Any] = None,
|
|
155
|
+
headers: Dict[str, str] = None,
|
|
139
156
|
) -> Dict[str, Any]:
|
|
140
|
-
"""Make an HTTP request to the API.
|
|
157
|
+
"""Make an async HTTP request to the API.
|
|
141
158
|
|
|
142
159
|
Args:
|
|
143
160
|
method: HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
144
161
|
path: API endpoint path.
|
|
145
162
|
query_params: Query parameters.
|
|
146
163
|
request_data: Request body data.
|
|
164
|
+
headers: Headers to override for this request.
|
|
147
165
|
|
|
148
166
|
Returns:
|
|
149
167
|
API response data.
|
|
@@ -153,41 +171,32 @@ class ShipthisAPI:
|
|
|
153
171
|
ShipthisRequestError: If the request fails.
|
|
154
172
|
"""
|
|
155
173
|
url = self.base_api_endpoint + path
|
|
156
|
-
|
|
174
|
+
request_headers = self._get_headers(headers)
|
|
157
175
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
response =
|
|
176
|
+
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
177
|
+
try:
|
|
178
|
+
response = await client.request(
|
|
161
179
|
method,
|
|
162
180
|
url,
|
|
163
|
-
|
|
164
|
-
headers=headers,
|
|
181
|
+
headers=request_headers,
|
|
165
182
|
params=query_params,
|
|
166
|
-
|
|
183
|
+
json=request_data,
|
|
167
184
|
)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
185
|
+
except httpx.TimeoutException:
|
|
186
|
+
raise ShipthisRequestError(
|
|
187
|
+
message="Request timed out",
|
|
188
|
+
status_code=408,
|
|
189
|
+
)
|
|
190
|
+
except httpx.ConnectError as e:
|
|
191
|
+
raise ShipthisRequestError(
|
|
192
|
+
message=f"Connection error: {str(e)}",
|
|
193
|
+
status_code=0,
|
|
194
|
+
)
|
|
195
|
+
except httpx.RequestError as e:
|
|
196
|
+
raise ShipthisRequestError(
|
|
197
|
+
message=f"Request failed: {str(e)}",
|
|
198
|
+
status_code=0,
|
|
175
199
|
)
|
|
176
|
-
except requests.exceptions.Timeout:
|
|
177
|
-
raise ShipthisRequestError(
|
|
178
|
-
message="Request timed out",
|
|
179
|
-
status_code=408,
|
|
180
|
-
)
|
|
181
|
-
except requests.exceptions.ConnectionError as e:
|
|
182
|
-
raise ShipthisRequestError(
|
|
183
|
-
message=f"Connection error: {str(e)}",
|
|
184
|
-
status_code=0,
|
|
185
|
-
)
|
|
186
|
-
except requests.exceptions.RequestException as e:
|
|
187
|
-
raise ShipthisRequestError(
|
|
188
|
-
message=f"Request failed: {str(e)}",
|
|
189
|
-
status_code=0,
|
|
190
|
-
)
|
|
191
200
|
|
|
192
201
|
# Handle authentication errors
|
|
193
202
|
if response.status_code == 401:
|
|
@@ -235,7 +244,7 @@ class ShipthisAPI:
|
|
|
235
244
|
|
|
236
245
|
# ==================== Connection ====================
|
|
237
246
|
|
|
238
|
-
def connect(self) -> Dict[str, Any]:
|
|
247
|
+
async def connect(self) -> Dict[str, Any]:
|
|
239
248
|
"""Connect and validate the API connection.
|
|
240
249
|
|
|
241
250
|
Fetches organisation info and validates region/location.
|
|
@@ -247,7 +256,7 @@ class ShipthisAPI:
|
|
|
247
256
|
Raises:
|
|
248
257
|
ShipthisAPIError: If connection fails.
|
|
249
258
|
"""
|
|
250
|
-
info = self.info()
|
|
259
|
+
info = await self.info()
|
|
251
260
|
self.organisation_info = info.get("organisation")
|
|
252
261
|
|
|
253
262
|
if not self.region_id or not self.location_id:
|
|
@@ -273,7 +282,7 @@ class ShipthisAPI:
|
|
|
273
282
|
|
|
274
283
|
# ==================== Info ====================
|
|
275
284
|
|
|
276
|
-
def info(self) -> Dict[str, Any]:
|
|
285
|
+
async def info(self) -> Dict[str, Any]:
|
|
277
286
|
"""Get organisation and user info.
|
|
278
287
|
|
|
279
288
|
Returns:
|
|
@@ -282,11 +291,11 @@ class ShipthisAPI:
|
|
|
282
291
|
Raises:
|
|
283
292
|
ShipthisAPIError: If the request fails.
|
|
284
293
|
"""
|
|
285
|
-
return self._make_request("GET", "user-auth/info")
|
|
294
|
+
return await self._make_request("GET", "user-auth/info")
|
|
286
295
|
|
|
287
296
|
# ==================== Collection CRUD ====================
|
|
288
297
|
|
|
289
|
-
def get_one_item(
|
|
298
|
+
async def get_one_item(
|
|
290
299
|
self,
|
|
291
300
|
collection_name: str,
|
|
292
301
|
doc_id: str = None,
|
|
@@ -312,19 +321,19 @@ class ShipthisAPI:
|
|
|
312
321
|
params = {}
|
|
313
322
|
if only_fields:
|
|
314
323
|
params["only"] = only_fields
|
|
315
|
-
return self._make_request("GET", path, query_params=params if params else None)
|
|
324
|
+
return await self._make_request("GET", path, query_params=params if params else None)
|
|
316
325
|
else:
|
|
317
326
|
params = {}
|
|
318
327
|
if filters:
|
|
319
328
|
params["query_filter_v2"] = json.dumps(filters)
|
|
320
329
|
if only_fields:
|
|
321
330
|
params["only"] = only_fields
|
|
322
|
-
resp = self._make_request("GET", f"incollection/{collection_name}", params)
|
|
331
|
+
resp = await self._make_request("GET", f"incollection/{collection_name}", params)
|
|
323
332
|
if isinstance(resp, dict) and resp.get("items"):
|
|
324
333
|
return resp.get("items")[0]
|
|
325
334
|
return None
|
|
326
335
|
|
|
327
|
-
def get_list(
|
|
336
|
+
async def get_list(
|
|
328
337
|
self,
|
|
329
338
|
collection_name: str,
|
|
330
339
|
filters: Dict[str, Any] = None,
|
|
@@ -369,13 +378,13 @@ class ShipthisAPI:
|
|
|
369
378
|
if not meta:
|
|
370
379
|
params["meta"] = "false"
|
|
371
380
|
|
|
372
|
-
response = self._make_request("GET", f"incollection/{collection_name}", params)
|
|
381
|
+
response = await self._make_request("GET", f"incollection/{collection_name}", params)
|
|
373
382
|
|
|
374
383
|
if isinstance(response, dict):
|
|
375
384
|
return response.get("items", [])
|
|
376
385
|
return []
|
|
377
386
|
|
|
378
|
-
def search(
|
|
387
|
+
async def search(
|
|
379
388
|
self,
|
|
380
389
|
collection_name: str,
|
|
381
390
|
query: str,
|
|
@@ -398,7 +407,7 @@ class ShipthisAPI:
|
|
|
398
407
|
Raises:
|
|
399
408
|
ShipthisAPIError: If the request fails.
|
|
400
409
|
"""
|
|
401
|
-
return self.get_list(
|
|
410
|
+
return await self.get_list(
|
|
402
411
|
collection_name,
|
|
403
412
|
search_query=query,
|
|
404
413
|
page=page,
|
|
@@ -406,7 +415,7 @@ class ShipthisAPI:
|
|
|
406
415
|
only_fields=only_fields,
|
|
407
416
|
)
|
|
408
417
|
|
|
409
|
-
def create_item(
|
|
418
|
+
async def create_item(
|
|
410
419
|
self, collection_name: str, data: Dict[str, Any]
|
|
411
420
|
) -> Dict[str, Any]:
|
|
412
421
|
"""Create a new item in a collection.
|
|
@@ -421,7 +430,7 @@ class ShipthisAPI:
|
|
|
421
430
|
Raises:
|
|
422
431
|
ShipthisAPIError: If the request fails.
|
|
423
432
|
"""
|
|
424
|
-
resp = self._make_request(
|
|
433
|
+
resp = await self._make_request(
|
|
425
434
|
"POST",
|
|
426
435
|
f"incollection/{collection_name}",
|
|
427
436
|
request_data={"reqbody": data},
|
|
@@ -430,7 +439,7 @@ class ShipthisAPI:
|
|
|
430
439
|
return resp.get("data")
|
|
431
440
|
return resp
|
|
432
441
|
|
|
433
|
-
def update_item(
|
|
442
|
+
async def update_item(
|
|
434
443
|
self,
|
|
435
444
|
collection_name: str,
|
|
436
445
|
object_id: str,
|
|
@@ -449,7 +458,7 @@ class ShipthisAPI:
|
|
|
449
458
|
Raises:
|
|
450
459
|
ShipthisAPIError: If the request fails.
|
|
451
460
|
"""
|
|
452
|
-
resp = self._make_request(
|
|
461
|
+
resp = await self._make_request(
|
|
453
462
|
"PUT",
|
|
454
463
|
f"incollection/{collection_name}/{object_id}",
|
|
455
464
|
request_data={"reqbody": updated_data},
|
|
@@ -458,7 +467,7 @@ class ShipthisAPI:
|
|
|
458
467
|
return resp.get("data")
|
|
459
468
|
return resp
|
|
460
469
|
|
|
461
|
-
def patch_item(
|
|
470
|
+
async def patch_item(
|
|
462
471
|
self,
|
|
463
472
|
collection_name: str,
|
|
464
473
|
object_id: str,
|
|
@@ -466,24 +475,34 @@ class ShipthisAPI:
|
|
|
466
475
|
) -> Dict[str, Any]:
|
|
467
476
|
"""Patch specific fields of an item.
|
|
468
477
|
|
|
478
|
+
This is the recommended way to update document fields. It goes through
|
|
479
|
+
full field validation, workflow triggers, audit logging, and business logic.
|
|
480
|
+
|
|
469
481
|
Args:
|
|
470
|
-
collection_name: Name of the collection.
|
|
482
|
+
collection_name: Name of the collection (e.g., "sea_shipment", "fcl_load").
|
|
471
483
|
object_id: Document ID.
|
|
472
|
-
update_fields:
|
|
484
|
+
update_fields: Dictionary of field_id to value mappings.
|
|
473
485
|
|
|
474
486
|
Returns:
|
|
475
487
|
Updated document data.
|
|
476
488
|
|
|
477
489
|
Raises:
|
|
478
490
|
ShipthisAPIError: If the request fails.
|
|
491
|
+
|
|
492
|
+
Example:
|
|
493
|
+
await client.patch_item(
|
|
494
|
+
"fcl_load",
|
|
495
|
+
"68a4f906743189ad061429a7",
|
|
496
|
+
update_fields={"container_no": "CONT123", "seal_no": "SEAL456"}
|
|
497
|
+
)
|
|
479
498
|
"""
|
|
480
|
-
return self._make_request(
|
|
499
|
+
return await self._make_request(
|
|
481
500
|
"PATCH",
|
|
482
501
|
f"incollection/{collection_name}/{object_id}",
|
|
483
502
|
request_data={"update_fields": update_fields},
|
|
484
503
|
)
|
|
485
504
|
|
|
486
|
-
def delete_item(self, collection_name: str, object_id: str) -> Dict[str, Any]:
|
|
505
|
+
async def delete_item(self, collection_name: str, object_id: str) -> Dict[str, Any]:
|
|
487
506
|
"""Delete an item.
|
|
488
507
|
|
|
489
508
|
Args:
|
|
@@ -496,14 +515,14 @@ class ShipthisAPI:
|
|
|
496
515
|
Raises:
|
|
497
516
|
ShipthisAPIError: If the request fails.
|
|
498
517
|
"""
|
|
499
|
-
return self._make_request(
|
|
518
|
+
return await self._make_request(
|
|
500
519
|
"DELETE",
|
|
501
520
|
f"incollection/{collection_name}/{object_id}",
|
|
502
521
|
)
|
|
503
522
|
|
|
504
523
|
# ==================== Workflow Operations ====================
|
|
505
524
|
|
|
506
|
-
def get_job_status(
|
|
525
|
+
async def get_job_status(
|
|
507
526
|
self, collection_name: str, object_id: str
|
|
508
527
|
) -> Dict[str, Any]:
|
|
509
528
|
"""Get the job status for a document.
|
|
@@ -518,12 +537,12 @@ class ShipthisAPI:
|
|
|
518
537
|
Raises:
|
|
519
538
|
ShipthisAPIError: If the request fails.
|
|
520
539
|
"""
|
|
521
|
-
return self._make_request(
|
|
540
|
+
return await self._make_request(
|
|
522
541
|
"GET",
|
|
523
542
|
f"workflow/{collection_name}/job_status/{object_id}",
|
|
524
543
|
)
|
|
525
544
|
|
|
526
|
-
def set_job_status(
|
|
545
|
+
async def set_job_status(
|
|
527
546
|
self,
|
|
528
547
|
collection_name: str,
|
|
529
548
|
object_id: str,
|
|
@@ -542,13 +561,13 @@ class ShipthisAPI:
|
|
|
542
561
|
Raises:
|
|
543
562
|
ShipthisAPIError: If the request fails.
|
|
544
563
|
"""
|
|
545
|
-
return self._make_request(
|
|
564
|
+
return await self._make_request(
|
|
546
565
|
"POST",
|
|
547
566
|
f"workflow/{collection_name}/job_status/{object_id}",
|
|
548
567
|
request_data={"action_index": action_index},
|
|
549
568
|
)
|
|
550
569
|
|
|
551
|
-
def get_workflow(self, object_id: str) -> Dict[str, Any]:
|
|
570
|
+
async def get_workflow(self, object_id: str) -> Dict[str, Any]:
|
|
552
571
|
"""Get a workflow configuration.
|
|
553
572
|
|
|
554
573
|
Args:
|
|
@@ -560,11 +579,11 @@ class ShipthisAPI:
|
|
|
560
579
|
Raises:
|
|
561
580
|
ShipthisAPIError: If the request fails.
|
|
562
581
|
"""
|
|
563
|
-
return self._make_request("GET", f"incollection/workflow/{object_id}")
|
|
582
|
+
return await self._make_request("GET", f"incollection/workflow/{object_id}")
|
|
564
583
|
|
|
565
584
|
# ==================== Reports ====================
|
|
566
585
|
|
|
567
|
-
def get_report_view(
|
|
586
|
+
async def get_report_view(
|
|
568
587
|
self,
|
|
569
588
|
report_name: str,
|
|
570
589
|
start_date: str,
|
|
@@ -598,7 +617,7 @@ class ShipthisAPI:
|
|
|
598
617
|
if self.location_id:
|
|
599
618
|
params["location"] = self.location_id
|
|
600
619
|
|
|
601
|
-
return self._make_request(
|
|
620
|
+
return await self._make_request(
|
|
602
621
|
"POST",
|
|
603
622
|
f"report-view/{report_name}",
|
|
604
623
|
query_params=params,
|
|
@@ -607,7 +626,7 @@ class ShipthisAPI:
|
|
|
607
626
|
|
|
608
627
|
# ==================== Third-party Services ====================
|
|
609
628
|
|
|
610
|
-
def get_exchange_rate(
|
|
629
|
+
async def get_exchange_rate(
|
|
611
630
|
self,
|
|
612
631
|
source_currency: str,
|
|
613
632
|
target_currency: str = "USD",
|
|
@@ -630,12 +649,12 @@ class ShipthisAPI:
|
|
|
630
649
|
if date is None:
|
|
631
650
|
date = int(time.time() * 1000)
|
|
632
651
|
|
|
633
|
-
return self._make_request(
|
|
652
|
+
return await self._make_request(
|
|
634
653
|
"GET",
|
|
635
654
|
f"thirdparty/currency?source={source_currency}&target={target_currency}&date={date}",
|
|
636
655
|
)
|
|
637
656
|
|
|
638
|
-
def autocomplete(
|
|
657
|
+
async def autocomplete(
|
|
639
658
|
self,
|
|
640
659
|
reference_name: str,
|
|
641
660
|
data: Dict[str, Any],
|
|
@@ -656,14 +675,14 @@ class ShipthisAPI:
|
|
|
656
675
|
if self.location_id:
|
|
657
676
|
params["location"] = self.location_id
|
|
658
677
|
|
|
659
|
-
return self._make_request(
|
|
678
|
+
return await self._make_request(
|
|
660
679
|
"POST",
|
|
661
680
|
f"autocomplete-reference/{reference_name}",
|
|
662
681
|
query_params=params if params else None,
|
|
663
682
|
request_data=data,
|
|
664
683
|
)
|
|
665
684
|
|
|
666
|
-
def search_location(self, query: str) -> List[Dict[str, Any]]:
|
|
685
|
+
async def search_location(self, query: str) -> List[Dict[str, Any]]:
|
|
667
686
|
"""Search for locations using Google Places.
|
|
668
687
|
|
|
669
688
|
Args:
|
|
@@ -675,12 +694,12 @@ class ShipthisAPI:
|
|
|
675
694
|
Raises:
|
|
676
695
|
ShipthisAPIError: If the request fails.
|
|
677
696
|
"""
|
|
678
|
-
return self._make_request(
|
|
697
|
+
return await self._make_request(
|
|
679
698
|
"GET",
|
|
680
699
|
f"thirdparty/search-place-autocomplete?query={query}",
|
|
681
700
|
)
|
|
682
701
|
|
|
683
|
-
def get_place_details(
|
|
702
|
+
async def get_place_details(
|
|
684
703
|
self,
|
|
685
704
|
place_id: str,
|
|
686
705
|
description: str = "",
|
|
@@ -697,14 +716,14 @@ class ShipthisAPI:
|
|
|
697
716
|
Raises:
|
|
698
717
|
ShipthisAPIError: If the request fails.
|
|
699
718
|
"""
|
|
700
|
-
return self._make_request(
|
|
719
|
+
return await self._make_request(
|
|
701
720
|
"GET",
|
|
702
721
|
f"thirdparty/select-google-place?query={place_id}&description={description}",
|
|
703
722
|
)
|
|
704
723
|
|
|
705
724
|
# ==================== Conversations ====================
|
|
706
725
|
|
|
707
|
-
def create_conversation(
|
|
726
|
+
async def create_conversation(
|
|
708
727
|
self,
|
|
709
728
|
view_name: str,
|
|
710
729
|
document_id: str,
|
|
@@ -729,9 +748,9 @@ class ShipthisAPI:
|
|
|
729
748
|
"view_name": view_name,
|
|
730
749
|
"message_type": conversation_data.get("type", ""),
|
|
731
750
|
}
|
|
732
|
-
return self._make_request("POST", "conversation", request_data=payload)
|
|
751
|
+
return await self._make_request("POST", "conversation", request_data=payload)
|
|
733
752
|
|
|
734
|
-
def get_conversations(
|
|
753
|
+
async def get_conversations(
|
|
735
754
|
self,
|
|
736
755
|
view_name: str,
|
|
737
756
|
document_id: str,
|
|
@@ -762,11 +781,11 @@ class ShipthisAPI:
|
|
|
762
781
|
"message_type": message_type,
|
|
763
782
|
"version": "2",
|
|
764
783
|
}
|
|
765
|
-
return self._make_request("GET", "conversation", query_params=params)
|
|
784
|
+
return await self._make_request("GET", "conversation", query_params=params)
|
|
766
785
|
|
|
767
786
|
# ==================== Bulk Operations ====================
|
|
768
787
|
|
|
769
|
-
def bulk_edit(
|
|
788
|
+
async def bulk_edit(
|
|
770
789
|
self,
|
|
771
790
|
collection_name: str,
|
|
772
791
|
ids: List[str],
|
|
@@ -788,7 +807,7 @@ class ShipthisAPI:
|
|
|
788
807
|
ShipthisAPIError: If the request fails.
|
|
789
808
|
|
|
790
809
|
Example:
|
|
791
|
-
client.bulk_edit(
|
|
810
|
+
await client.bulk_edit(
|
|
792
811
|
"customer",
|
|
793
812
|
ids=["5fdc00487f7636c97b9fa064", "608fe19fc33215427867f34e"],
|
|
794
813
|
update_data={"company.fax_no": "12323231", "address.state": "California"}
|
|
@@ -803,7 +822,7 @@ class ShipthisAPI:
|
|
|
803
822
|
if external_update_data:
|
|
804
823
|
payload["data"]["external_update_data"] = external_update_data
|
|
805
824
|
|
|
806
|
-
return self._make_request(
|
|
825
|
+
return await self._make_request(
|
|
807
826
|
"POST",
|
|
808
827
|
f"incollection_group_edit/{collection_name}",
|
|
809
828
|
request_data=payload,
|
|
@@ -811,7 +830,7 @@ class ShipthisAPI:
|
|
|
811
830
|
|
|
812
831
|
# ==================== Workflow Actions ====================
|
|
813
832
|
|
|
814
|
-
def primary_workflow_action(
|
|
833
|
+
async def primary_workflow_action(
|
|
815
834
|
self,
|
|
816
835
|
collection: str,
|
|
817
836
|
workflow_id: str,
|
|
@@ -837,7 +856,7 @@ class ShipthisAPI:
|
|
|
837
856
|
ShipthisAPIError: If the request fails.
|
|
838
857
|
|
|
839
858
|
Example:
|
|
840
|
-
client.primary_workflow_action(
|
|
859
|
+
await client.primary_workflow_action(
|
|
841
860
|
collection="pickup_delivery",
|
|
842
861
|
workflow_id="job_status",
|
|
843
862
|
object_id="68a4f906743189ad061429a7",
|
|
@@ -853,13 +872,13 @@ class ShipthisAPI:
|
|
|
853
872
|
if start_state_id:
|
|
854
873
|
payload["start_state_id"] = start_state_id
|
|
855
874
|
|
|
856
|
-
return self._make_request(
|
|
875
|
+
return await self._make_request(
|
|
857
876
|
"POST",
|
|
858
877
|
f"workflow/{collection}/{workflow_id}/{object_id}",
|
|
859
878
|
request_data=payload,
|
|
860
879
|
)
|
|
861
880
|
|
|
862
|
-
def secondary_workflow_action(
|
|
881
|
+
async def secondary_workflow_action(
|
|
863
882
|
self,
|
|
864
883
|
collection: str,
|
|
865
884
|
workflow_id: str,
|
|
@@ -883,7 +902,7 @@ class ShipthisAPI:
|
|
|
883
902
|
ShipthisAPIError: If the request fails.
|
|
884
903
|
|
|
885
904
|
Example:
|
|
886
|
-
client.secondary_workflow_action(
|
|
905
|
+
await client.secondary_workflow_action(
|
|
887
906
|
collection="pickup_delivery",
|
|
888
907
|
workflow_id="driver_status",
|
|
889
908
|
object_id="67ed10859b7cf551a19f813e",
|
|
@@ -892,7 +911,7 @@ class ShipthisAPI:
|
|
|
892
911
|
"""
|
|
893
912
|
payload = additional_data or {}
|
|
894
913
|
|
|
895
|
-
return self._make_request(
|
|
914
|
+
return await self._make_request(
|
|
896
915
|
"POST",
|
|
897
916
|
f"workflow/{collection}/{workflow_id}/{object_id}/{target_state}",
|
|
898
917
|
request_data=payload,
|
|
@@ -900,7 +919,7 @@ class ShipthisAPI:
|
|
|
900
919
|
|
|
901
920
|
# ==================== File Upload ====================
|
|
902
921
|
|
|
903
|
-
def upload_file(
|
|
922
|
+
async def upload_file(
|
|
904
923
|
self,
|
|
905
924
|
file_path: str,
|
|
906
925
|
file_name: str = None,
|
|
@@ -933,18 +952,18 @@ class ShipthisAPI:
|
|
|
933
952
|
try:
|
|
934
953
|
with open(file_path, "rb") as f:
|
|
935
954
|
files = {"file": (file_name, f)}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
955
|
+
async with httpx.AsyncClient(timeout=self.timeout * 2) as client:
|
|
956
|
+
response = await client.post(
|
|
957
|
+
upload_url,
|
|
958
|
+
headers=headers,
|
|
959
|
+
files=files,
|
|
960
|
+
)
|
|
942
961
|
except FileNotFoundError:
|
|
943
962
|
raise ShipthisRequestError(
|
|
944
963
|
message=f"File not found: {file_path}",
|
|
945
964
|
status_code=0,
|
|
946
965
|
)
|
|
947
|
-
except
|
|
966
|
+
except httpx.RequestError as e:
|
|
948
967
|
raise ShipthisRequestError(
|
|
949
968
|
message=f"Upload failed: {str(e)}",
|
|
950
969
|
status_code=0,
|
|
@@ -959,4 +978,38 @@ class ShipthisAPI:
|
|
|
959
978
|
raise ShipthisRequestError(
|
|
960
979
|
message=f"Upload failed with status {response.status_code}",
|
|
961
980
|
status_code=response.status_code,
|
|
962
|
-
)
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
# ==================== Reference Linked Fields ====================
|
|
984
|
+
|
|
985
|
+
async def create_reference_linked_field(
|
|
986
|
+
self,
|
|
987
|
+
collection_name: str,
|
|
988
|
+
doc_id: str,
|
|
989
|
+
payload: Dict[str, Any],
|
|
990
|
+
) -> Dict[str, Any]:
|
|
991
|
+
"""Create a reference-linked field on a document.
|
|
992
|
+
|
|
993
|
+
Args:
|
|
994
|
+
collection_name: Collection name.
|
|
995
|
+
doc_id: Document ID.
|
|
996
|
+
payload: Field data to create.
|
|
997
|
+
|
|
998
|
+
Returns:
|
|
999
|
+
API response.
|
|
1000
|
+
|
|
1001
|
+
Raises:
|
|
1002
|
+
ShipthisAPIError: If the request fails.
|
|
1003
|
+
|
|
1004
|
+
Example:
|
|
1005
|
+
await client.create_reference_linked_field(
|
|
1006
|
+
"sea_shipment",
|
|
1007
|
+
"68a4f906743189ad061429a7",
|
|
1008
|
+
payload={"field_name": "containers", "data": {...}}
|
|
1009
|
+
)
|
|
1010
|
+
"""
|
|
1011
|
+
return await self._make_request(
|
|
1012
|
+
"POST",
|
|
1013
|
+
f"incollection/create-reference-linked-field/{collection_name}/{doc_id}",
|
|
1014
|
+
request_data=payload,
|
|
1015
|
+
)
|
|
@@ -5,15 +5,18 @@ with open("README.md", "r") as fh:
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
setuptools.setup(
|
|
8
|
-
name='shipthisapi-python',
|
|
9
|
-
version='
|
|
8
|
+
name='shipthisapi-python',
|
|
9
|
+
version='3.0.0',
|
|
10
10
|
author="Mayur Rawte",
|
|
11
11
|
author_email="mayur@shipthis.co",
|
|
12
|
-
description="ShipthisAPI utility package",
|
|
12
|
+
description="ShipthisAPI async utility package",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
15
15
|
url="https://github.com/shipthisco/shipthisapi-python",
|
|
16
16
|
packages=setuptools.find_packages(),
|
|
17
|
+
install_requires=[
|
|
18
|
+
"httpx>=0.24.0",
|
|
19
|
+
],
|
|
17
20
|
classifiers=[
|
|
18
21
|
"Programming Language :: Python :: 3",
|
|
19
22
|
"License :: OSI Approved :: MIT License",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shipthisapi-python
|
|
3
|
-
Version:
|
|
4
|
-
Summary: ShipthisAPI utility package
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: ShipthisAPI async utility package
|
|
5
5
|
Home-page: https://github.com/shipthisco/shipthisapi-python
|
|
6
6
|
Author: Mayur Rawte
|
|
7
7
|
Author-email: mayur@shipthis.co
|
|
@@ -9,12 +9,14 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: httpx>=0.24.0
|
|
12
13
|
Dynamic: author
|
|
13
14
|
Dynamic: author-email
|
|
14
15
|
Dynamic: classifier
|
|
15
16
|
Dynamic: description
|
|
16
17
|
Dynamic: description-content-type
|
|
17
18
|
Dynamic: home-page
|
|
19
|
+
Dynamic: requires-dist
|
|
18
20
|
Dynamic: summary
|
|
19
21
|
|
|
20
22
|
# ShipthisAPI Python
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx>=0.24.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shipthisapi_python-2.1.0 → shipthisapi_python-3.0.0}/shipthisapi_python.egg-info/top_level.txt
RENAMED
|
File without changes
|