karrio-eshipper 2025.5.6__py3-none-any.whl → 2026.1__py3-none-any.whl
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.
- karrio/mappers/eshipper/proxy.py +67 -4
- karrio/providers/eshipper/rate.py +3 -0
- karrio/providers/eshipper/shipment/create.py +16 -0
- karrio/providers/eshipper/tracking.py +17 -0
- karrio/providers/eshipper/units.py +63 -15
- karrio/providers/eshipper/utils.py +2 -66
- {karrio_eshipper-2025.5.6.dist-info → karrio_eshipper-2026.1.dist-info}/METADATA +1 -1
- {karrio_eshipper-2025.5.6.dist-info → karrio_eshipper-2026.1.dist-info}/RECORD +11 -11
- {karrio_eshipper-2025.5.6.dist-info → karrio_eshipper-2026.1.dist-info}/WHEEL +0 -0
- {karrio_eshipper-2025.5.6.dist-info → karrio_eshipper-2026.1.dist-info}/entry_points.txt +0 -0
- {karrio_eshipper-2025.5.6.dist-info → karrio_eshipper-2026.1.dist-info}/top_level.txt +0 -0
karrio/mappers/eshipper/proxy.py
CHANGED
|
@@ -1,14 +1,74 @@
|
|
|
1
1
|
"""Karrio eShipper client proxy."""
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
3
4
|
import karrio.lib as lib
|
|
4
5
|
import karrio.api.proxy as proxy
|
|
6
|
+
import karrio.core.errors as errors
|
|
7
|
+
import karrio.core.models as models
|
|
8
|
+
import karrio.providers.eshipper.error as provider_error
|
|
5
9
|
import karrio.mappers.eshipper.settings as provider_settings
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
class Proxy(proxy.Proxy):
|
|
9
13
|
settings: provider_settings.Settings
|
|
10
14
|
|
|
15
|
+
def authenticate(self, _=None) -> lib.Deserializable[str]:
|
|
16
|
+
"""Retrieve the token using the principal|credential pair
|
|
17
|
+
or collect it from the cache if an unexpired token exist.
|
|
18
|
+
"""
|
|
19
|
+
cache_key = f"{self.settings.carrier_name}|{self.settings.principal}|{self.settings.credential}"
|
|
20
|
+
|
|
21
|
+
def get_token():
|
|
22
|
+
result = lib.request(
|
|
23
|
+
url=f"{self.settings.server_url}/api/v2/authenticate",
|
|
24
|
+
trace=self.settings.trace_as("json"),
|
|
25
|
+
method="POST",
|
|
26
|
+
headers={"content-Type": "application/json"},
|
|
27
|
+
data=lib.to_json(
|
|
28
|
+
{
|
|
29
|
+
"principal": self.settings.principal,
|
|
30
|
+
"credential": self.settings.credential,
|
|
31
|
+
}
|
|
32
|
+
),
|
|
33
|
+
max_retries=2,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
response = lib.to_dict(result)
|
|
37
|
+
messages = provider_error.parse_error_response(response, self.settings)
|
|
38
|
+
|
|
39
|
+
if any(messages):
|
|
40
|
+
raise errors.ParsedMessagesError(messages=messages)
|
|
41
|
+
|
|
42
|
+
# Validate that token is present in the response
|
|
43
|
+
if "token" not in response:
|
|
44
|
+
raise errors.ParsedMessagesError(
|
|
45
|
+
messages=[
|
|
46
|
+
models.Message(
|
|
47
|
+
carrier_name=self.settings.carrier_name,
|
|
48
|
+
carrier_id=self.settings.carrier_id,
|
|
49
|
+
message="Authentication failed: No token received",
|
|
50
|
+
code="AUTH_ERROR",
|
|
51
|
+
)
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
expiry = datetime.datetime.now() + datetime.timedelta(
|
|
56
|
+
seconds=float(response.get("expires_in", 0))
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return {**response, "expiry": lib.fdatetime(expiry)}
|
|
60
|
+
|
|
61
|
+
token = self.settings.connection_cache.thread_safe(
|
|
62
|
+
refresh_func=get_token,
|
|
63
|
+
cache_key=cache_key,
|
|
64
|
+
buffer_minutes=30,
|
|
65
|
+
token_field="token",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return lib.Deserializable(token.get_state())
|
|
69
|
+
|
|
11
70
|
def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
|
71
|
+
access_token = self.authenticate().deserialize()
|
|
12
72
|
response = lib.request(
|
|
13
73
|
url=f"{self.settings.server_url}/api/v2/quote",
|
|
14
74
|
data=lib.to_json(request.serialize()),
|
|
@@ -16,13 +76,14 @@ class Proxy(proxy.Proxy):
|
|
|
16
76
|
method="POST",
|
|
17
77
|
headers={
|
|
18
78
|
"content-Type": "application/json",
|
|
19
|
-
"Authorization": f"Bearer {
|
|
79
|
+
"Authorization": f"Bearer {access_token}",
|
|
20
80
|
},
|
|
21
81
|
)
|
|
22
82
|
|
|
23
83
|
return lib.Deserializable(response, lib.to_dict)
|
|
24
84
|
|
|
25
85
|
def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
|
86
|
+
access_token = self.authenticate().deserialize()
|
|
26
87
|
response = lib.request(
|
|
27
88
|
url=f"{self.settings.server_url}/api/v2/ship",
|
|
28
89
|
data=lib.to_json(request.serialize()),
|
|
@@ -30,13 +91,14 @@ class Proxy(proxy.Proxy):
|
|
|
30
91
|
method="PUT",
|
|
31
92
|
headers={
|
|
32
93
|
"content-Type": "application/json",
|
|
33
|
-
"Authorization": f"Bearer {
|
|
94
|
+
"Authorization": f"Bearer {access_token}",
|
|
34
95
|
},
|
|
35
96
|
)
|
|
36
97
|
|
|
37
98
|
return lib.Deserializable(response, lib.to_dict)
|
|
38
99
|
|
|
39
100
|
def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
|
101
|
+
access_token = self.authenticate().deserialize()
|
|
40
102
|
response = lib.request(
|
|
41
103
|
url=f"{self.settings.server_url}/api/v2/ship/cancel",
|
|
42
104
|
data=lib.to_json(request.serialize()),
|
|
@@ -44,13 +106,14 @@ class Proxy(proxy.Proxy):
|
|
|
44
106
|
method="DELETE",
|
|
45
107
|
headers={
|
|
46
108
|
"content-Type": "application/json",
|
|
47
|
-
"Authorization": f"Bearer {
|
|
109
|
+
"Authorization": f"Bearer {access_token}",
|
|
48
110
|
},
|
|
49
111
|
)
|
|
50
112
|
|
|
51
113
|
return lib.Deserializable(response, lib.to_dict)
|
|
52
114
|
|
|
53
115
|
def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
|
116
|
+
access_token = self.authenticate().deserialize()
|
|
54
117
|
query = lib.to_query_string(request.serialize())
|
|
55
118
|
response = lib.request(
|
|
56
119
|
url=f"{self.settings.server_url}/api/v2/track/events?{query}",
|
|
@@ -58,7 +121,7 @@ class Proxy(proxy.Proxy):
|
|
|
58
121
|
method="GET",
|
|
59
122
|
headers={
|
|
60
123
|
"content-Type": "application/json",
|
|
61
|
-
"Authorization": f"Bearer {
|
|
124
|
+
"Authorization": f"Bearer {access_token}",
|
|
62
125
|
},
|
|
63
126
|
)
|
|
64
127
|
|
|
@@ -30,18 +30,21 @@ def _extract_details(
|
|
|
30
30
|
rate.serviceName,
|
|
31
31
|
service_id=rate.serviceId,
|
|
32
32
|
test_mode=settings.test_mode,
|
|
33
|
+
carrier_name=rate.carrierName,
|
|
33
34
|
)
|
|
34
35
|
carrier_id = provider_units.ShippingService.carrier_id(
|
|
35
36
|
rate.carrierName,
|
|
36
37
|
test_mode=settings.test_mode,
|
|
37
38
|
service_search=service.name_or_key,
|
|
38
39
|
service_id=rate.serviceId,
|
|
40
|
+
carrier_name=rate.carrierName,
|
|
39
41
|
)
|
|
40
42
|
rate_provider = provider_units.RateProvider.find(
|
|
41
43
|
rate.carrierName,
|
|
42
44
|
test_mode=settings.test_mode,
|
|
43
45
|
service_search=service.name_or_key,
|
|
44
46
|
service_id=rate.serviceId,
|
|
47
|
+
carrier_name=rate.carrierName,
|
|
45
48
|
)
|
|
46
49
|
|
|
47
50
|
charges = [
|
|
@@ -34,10 +34,12 @@ def _extract_details(
|
|
|
34
34
|
service = provider_units.ShippingService.find(
|
|
35
35
|
shipment.carrier.serviceName,
|
|
36
36
|
test_mode=settings.test_mode,
|
|
37
|
+
carrier_name=shipment.carrier.carrierName,
|
|
37
38
|
)
|
|
38
39
|
rate_provider = provider_units.RateProvider.find(
|
|
39
40
|
shipment.carrier.carrierName,
|
|
40
41
|
test_mode=settings.test_mode,
|
|
42
|
+
carrier_name=shipment.carrier.carrierName,
|
|
41
43
|
)
|
|
42
44
|
|
|
43
45
|
return models.ShipmentDetails(
|
|
@@ -89,11 +91,24 @@ def shipment_request(
|
|
|
89
91
|
initializer=provider_units.shipping_options_initializer,
|
|
90
92
|
)
|
|
91
93
|
service = provider_units.ShippingService.map(payload.service)
|
|
94
|
+
|
|
95
|
+
# Extract carrier from service code if available
|
|
96
|
+
carrier_name_from_service = None
|
|
97
|
+
if "_" in service.name_or_key:
|
|
98
|
+
parts = service.name_or_key.split("_")
|
|
99
|
+
# Carrier is always at index 1 after "eshipper" prefix
|
|
100
|
+
if len(parts) >= 2 and parts[0] == "eshipper":
|
|
101
|
+
potential_carrier = parts[1] # e.g., "canadapost" from "eshipper_canadapost_expedited"
|
|
102
|
+
# Validate it's a known carrier
|
|
103
|
+
if provider_units.RateProvider.map(potential_carrier).name:
|
|
104
|
+
carrier_name_from_service = potential_carrier
|
|
105
|
+
|
|
92
106
|
service_id = lib.identity(
|
|
93
107
|
options.eshipper_service_id.state
|
|
94
108
|
or provider_units.ShippingService.service_id(
|
|
95
109
|
service.name_or_key,
|
|
96
110
|
test_mode=settings.test_mode,
|
|
111
|
+
carrier_name=carrier_name_from_service,
|
|
97
112
|
)
|
|
98
113
|
)
|
|
99
114
|
carrier_id = lib.identity(
|
|
@@ -103,6 +118,7 @@ def shipment_request(
|
|
|
103
118
|
service_id=service_id,
|
|
104
119
|
test_mode=settings.test_mode,
|
|
105
120
|
service_search=service.name_or_key,
|
|
121
|
+
carrier_name=carrier_name_from_service,
|
|
106
122
|
)
|
|
107
123
|
)
|
|
108
124
|
|
|
@@ -51,6 +51,23 @@ def _extract_details(
|
|
|
51
51
|
description=event.description,
|
|
52
52
|
date=lib.fdate(event.originalEvent.eventDate, "%Y-%m-%d %H:%M:%S"),
|
|
53
53
|
time=lib.flocaltime(event.originalEvent.eventDate, "%Y-%m-%d %H:%M:%S"),
|
|
54
|
+
timestamp=lib.fiso_timestamp(event.originalEvent.eventDate, current_format="%Y-%m-%d %H:%M:%S"),
|
|
55
|
+
status=next(
|
|
56
|
+
(
|
|
57
|
+
s.name
|
|
58
|
+
for s in list(provider_units.TrackingStatus)
|
|
59
|
+
if event.originalEvent.name in s.value
|
|
60
|
+
),
|
|
61
|
+
None,
|
|
62
|
+
),
|
|
63
|
+
reason=next(
|
|
64
|
+
(
|
|
65
|
+
r.name
|
|
66
|
+
for r in list(provider_units.TrackingIncidentReason)
|
|
67
|
+
if event.originalEvent.name in r.value
|
|
68
|
+
),
|
|
69
|
+
None,
|
|
70
|
+
),
|
|
54
71
|
)
|
|
55
72
|
for event in details.event
|
|
56
73
|
],
|
|
@@ -102,6 +102,14 @@ class TrackingStatus(lib.Enum):
|
|
|
102
102
|
ready_for_pickup = ["ready_for_pickup"]
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
class TrackingIncidentReason(lib.Enum):
|
|
106
|
+
"""Maps eShipper exception codes to normalized TrackingIncidentReason."""
|
|
107
|
+
carrier_damaged_parcel = []
|
|
108
|
+
consignee_refused = []
|
|
109
|
+
consignee_not_home = []
|
|
110
|
+
unknown = []
|
|
111
|
+
|
|
112
|
+
|
|
105
113
|
def to_carrier_code(carrierDTO: typing.Dict[str, str]) -> str:
|
|
106
114
|
_code = lib.to_snake_case((carrierDTO or {}).get("name") or "eshipper")
|
|
107
115
|
|
|
@@ -132,7 +140,12 @@ def to_service_code(service: typing.Dict[str, str]) -> str:
|
|
|
132
140
|
return output
|
|
133
141
|
|
|
134
142
|
|
|
135
|
-
def get_service(
|
|
143
|
+
def get_service(
|
|
144
|
+
search: str,
|
|
145
|
+
test_mode: bool = False,
|
|
146
|
+
service_id: str = None,
|
|
147
|
+
carrier_name: str = None,
|
|
148
|
+
):
|
|
136
149
|
prod_metadata = METADATA_JSON["PROD_SERVICES"]
|
|
137
150
|
test_metadata = METADATA_JSON["DEV_SERVICES"]
|
|
138
151
|
metadata = lib.identity(
|
|
@@ -144,27 +157,53 @@ def get_service(search: str, test_mode: bool = False, service_id: str = None):
|
|
|
144
157
|
service
|
|
145
158
|
for service in metadata
|
|
146
159
|
if to_service_code(service) == search
|
|
147
|
-
or service.get("name") == search
|
|
148
160
|
or str(service.get("id")) == search
|
|
149
161
|
or (service_id and service_id == str(service.get("id")))
|
|
162
|
+
or (
|
|
163
|
+
service.get("name") == search
|
|
164
|
+
and (
|
|
165
|
+
not carrier_name
|
|
166
|
+
or service.get("carrierDTO", {}).get("name") == carrier_name
|
|
167
|
+
)
|
|
168
|
+
)
|
|
150
169
|
),
|
|
151
170
|
{},
|
|
152
171
|
)
|
|
153
172
|
|
|
154
173
|
|
|
155
|
-
def get_service_id(
|
|
174
|
+
def get_service_id(
|
|
175
|
+
search: str,
|
|
176
|
+
test_mode: bool = False,
|
|
177
|
+
service_id: str = None,
|
|
178
|
+
carrier_name: str = None,
|
|
179
|
+
):
|
|
156
180
|
return (
|
|
157
|
-
get_service(
|
|
181
|
+
get_service(
|
|
182
|
+
search,
|
|
183
|
+
test_mode=test_mode,
|
|
184
|
+
service_id=service_id,
|
|
185
|
+
carrier_name=carrier_name,
|
|
186
|
+
).get("id")
|
|
158
187
|
or service_id
|
|
159
188
|
)
|
|
160
189
|
|
|
161
190
|
|
|
162
|
-
def find_service(
|
|
191
|
+
def find_service(
|
|
192
|
+
search: str,
|
|
193
|
+
test_mode: bool = False,
|
|
194
|
+
service_id: str = None,
|
|
195
|
+
carrier_name: str = None,
|
|
196
|
+
):
|
|
163
197
|
|
|
164
198
|
if ShippingService.map(search).name:
|
|
165
199
|
return ShippingService.map(search)
|
|
166
200
|
|
|
167
|
-
service = get_service(
|
|
201
|
+
service = get_service(
|
|
202
|
+
search,
|
|
203
|
+
test_mode=test_mode,
|
|
204
|
+
service_id=service_id,
|
|
205
|
+
carrier_name=carrier_name,
|
|
206
|
+
)
|
|
168
207
|
|
|
169
208
|
if service:
|
|
170
209
|
return ShippingService.map(to_service_code(service))
|
|
@@ -177,10 +216,16 @@ def get_carrier(
|
|
|
177
216
|
test_mode: bool = False,
|
|
178
217
|
service_search: str = None,
|
|
179
218
|
service_id: str = None,
|
|
219
|
+
carrier_name: str = None,
|
|
180
220
|
):
|
|
181
221
|
id_key = "test_id" if test_mode else "prod_id"
|
|
182
222
|
alternate_key = "prod_id" if not test_mode else "test_id"
|
|
183
|
-
service = get_service(
|
|
223
|
+
service = get_service(
|
|
224
|
+
service_search,
|
|
225
|
+
test_mode=test_mode,
|
|
226
|
+
service_id=service_id,
|
|
227
|
+
carrier_name=carrier_name or search,
|
|
228
|
+
)
|
|
184
229
|
|
|
185
230
|
return service.get("carrierDTO") or next(
|
|
186
231
|
(
|
|
@@ -204,12 +249,14 @@ def get_carrier_id(
|
|
|
204
249
|
test_mode: bool = False,
|
|
205
250
|
service_search: str = None,
|
|
206
251
|
service_id: str = None,
|
|
252
|
+
carrier_name: str = None,
|
|
207
253
|
):
|
|
208
254
|
return get_carrier(
|
|
209
255
|
search,
|
|
210
256
|
test_mode=test_mode,
|
|
211
257
|
service_search=service_search,
|
|
212
258
|
service_id=service_id,
|
|
259
|
+
carrier_name=carrier_name,
|
|
213
260
|
).get("id")
|
|
214
261
|
|
|
215
262
|
|
|
@@ -218,6 +265,7 @@ def find_rate_provider(
|
|
|
218
265
|
test_mode: bool = False,
|
|
219
266
|
service_search: str = None,
|
|
220
267
|
service_id: str = None,
|
|
268
|
+
carrier_name: str = None,
|
|
221
269
|
):
|
|
222
270
|
|
|
223
271
|
if RateProvider.map(lib.to_snake_case(search)).name:
|
|
@@ -228,6 +276,7 @@ def find_rate_provider(
|
|
|
228
276
|
test_mode=test_mode,
|
|
229
277
|
service_search=service_search,
|
|
230
278
|
service_id=service_id,
|
|
279
|
+
carrier_name=carrier_name,
|
|
231
280
|
)
|
|
232
281
|
|
|
233
282
|
if carrier and RateProvider.map(to_carrier_code(carrier)).name:
|
|
@@ -273,7 +322,7 @@ ESHIPPER_CARRIER_METADATA = {
|
|
|
273
322
|
}
|
|
274
323
|
|
|
275
324
|
ESHIPPER_SERVICE_METADATA = {
|
|
276
|
-
|
|
325
|
+
to_service_code(service): {
|
|
277
326
|
**service,
|
|
278
327
|
"ids": list(
|
|
279
328
|
set(
|
|
@@ -281,8 +330,7 @@ ESHIPPER_SERVICE_METADATA = {
|
|
|
281
330
|
s["id"]
|
|
282
331
|
for s in METADATA_JSON["PROD_SERVICES"]
|
|
283
332
|
+ METADATA_JSON["DEV_SERVICES"]
|
|
284
|
-
if
|
|
285
|
-
== lib.to_snake_case(service["name"])
|
|
333
|
+
if to_service_code(s) == to_service_code(service)
|
|
286
334
|
]
|
|
287
335
|
)
|
|
288
336
|
),
|
|
@@ -290,7 +338,7 @@ ESHIPPER_SERVICE_METADATA = {
|
|
|
290
338
|
(
|
|
291
339
|
s["id"]
|
|
292
340
|
for s in METADATA_JSON["PROD_SERVICES"]
|
|
293
|
-
if s
|
|
341
|
+
if to_service_code(s) == to_service_code(service)
|
|
294
342
|
),
|
|
295
343
|
None,
|
|
296
344
|
),
|
|
@@ -298,15 +346,15 @@ ESHIPPER_SERVICE_METADATA = {
|
|
|
298
346
|
(
|
|
299
347
|
s["id"]
|
|
300
348
|
for s in METADATA_JSON["DEV_SERVICES"]
|
|
301
|
-
if s
|
|
349
|
+
if to_service_code(s) == to_service_code(service)
|
|
302
350
|
),
|
|
303
351
|
None,
|
|
304
352
|
),
|
|
305
353
|
"carrier": lib.to_snake_case(service["carrierDTO"]["name"]),
|
|
306
354
|
}
|
|
307
355
|
for service in {
|
|
308
|
-
s
|
|
309
|
-
for s in METADATA_JSON["
|
|
356
|
+
to_service_code(s): s
|
|
357
|
+
for s in METADATA_JSON["DEV_SERVICES"] + METADATA_JSON["PROD_SERVICES"]
|
|
310
358
|
}.values()
|
|
311
359
|
}
|
|
312
360
|
|
|
@@ -314,7 +362,7 @@ ESHIPPER_SERVICE_METADATA = {
|
|
|
314
362
|
ShippingService = lib.StrEnum(
|
|
315
363
|
"ShippingService",
|
|
316
364
|
{
|
|
317
|
-
to_service_code(service): service
|
|
365
|
+
to_service_code(service): to_service_code(service)
|
|
318
366
|
for service in ESHIPPER_SERVICE_METADATA.values()
|
|
319
367
|
},
|
|
320
368
|
)
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"""eShipper connection settings."""
|
|
2
|
+
|
|
3
3
|
import karrio.core as core
|
|
4
|
-
import karrio.core.errors as errors
|
|
5
|
-
import karrio.core.models as models
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
class Settings(core.Settings):
|
|
@@ -20,65 +18,3 @@ class Settings(core.Settings):
|
|
|
20
18
|
return (
|
|
21
19
|
"https://uu2.eshipper.com" if self.test_mode else "https://ww2.eshipper.com"
|
|
22
20
|
)
|
|
23
|
-
|
|
24
|
-
@property
|
|
25
|
-
def access_token(self):
|
|
26
|
-
"""Retrieve the "token" using the principal|credential pair
|
|
27
|
-
or collect it from the cache if an unexpired "token" exist.
|
|
28
|
-
"""
|
|
29
|
-
cache_key = f"{self.carrier_name}|{self.principal}|{self.credential}"
|
|
30
|
-
|
|
31
|
-
return self.connection_cache.thread_safe(
|
|
32
|
-
refresh_func=lambda: login(self),
|
|
33
|
-
cache_key=cache_key,
|
|
34
|
-
buffer_minutes=30,
|
|
35
|
-
token_field="token",
|
|
36
|
-
).get_state()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def login(settings: Settings):
|
|
40
|
-
"""Sign in response
|
|
41
|
-
{
|
|
42
|
-
"token": "string",
|
|
43
|
-
"expires_in": "string",
|
|
44
|
-
"token_type": "string",
|
|
45
|
-
"refresh_token": "string",
|
|
46
|
-
"refresh_expires_in": "string"
|
|
47
|
-
}
|
|
48
|
-
"""
|
|
49
|
-
import karrio.providers.eshipper.error as error
|
|
50
|
-
|
|
51
|
-
result = lib.request(
|
|
52
|
-
url=f"{settings.server_url}/api/v2/authenticate",
|
|
53
|
-
data=lib.to_json(
|
|
54
|
-
{
|
|
55
|
-
"principal": settings.principal,
|
|
56
|
-
"credential": settings.credential,
|
|
57
|
-
}
|
|
58
|
-
),
|
|
59
|
-
method="POST",
|
|
60
|
-
headers={"content-Type": "application/json"},
|
|
61
|
-
)
|
|
62
|
-
response = lib.to_dict(result)
|
|
63
|
-
messages = error.parse_error_response(response, settings)
|
|
64
|
-
|
|
65
|
-
if any(messages):
|
|
66
|
-
raise errors.ParsedMessagesError(messages=messages)
|
|
67
|
-
|
|
68
|
-
# Validate that token is present in the response
|
|
69
|
-
if "token" not in response:
|
|
70
|
-
raise errors.ParsedMessagesError(
|
|
71
|
-
messages=[
|
|
72
|
-
models.Message(
|
|
73
|
-
carrier_name=settings.carrier_name,
|
|
74
|
-
carrier_id=settings.carrier_id,
|
|
75
|
-
message="Authentication failed: No token received",
|
|
76
|
-
code="AUTH_ERROR",
|
|
77
|
-
)
|
|
78
|
-
]
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
expiry = datetime.datetime.now() + datetime.timedelta(
|
|
82
|
-
seconds=float(response.get("expires_in", 0))
|
|
83
|
-
)
|
|
84
|
-
return {**response, "expiry": lib.fdatetime(expiry)}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
karrio/mappers/eshipper/__init__.py,sha256=klSnHWetpSpOBsj7OgncePwLbSK1etgRQN_PZSK2pUo,152
|
|
2
2
|
karrio/mappers/eshipper/mapper.py,sha256=oFs6tqFI9vbm13sh8hHoZVxA2VrnqdaixOxeB9nnJeY,1969
|
|
3
|
-
karrio/mappers/eshipper/proxy.py,sha256=
|
|
3
|
+
karrio/mappers/eshipper/proxy.py,sha256=2U683Jl_Prr1Rov8fbtuUyhJHWGA-cxkJPHLVMvYIk8,4770
|
|
4
4
|
karrio/mappers/eshipper/settings.py,sha256=ZN7DTMBMTC_JtOgUFQ2UNCyU5j-VUfv2us_F90fL1jw,491
|
|
5
5
|
karrio/plugins/eshipper/__init__.py,sha256=v5cNriCwL5R2QR5x6Iki23CcBGPmGmH8zEL5j02u9Ko,448
|
|
6
6
|
karrio/providers/eshipper/__init__.py,sha256=Y9vKPYScbmFvM17L-x52m_yLLiG6WsX9j6o4NIERBMU,400
|
|
7
7
|
karrio/providers/eshipper/error.py,sha256=uroZ2dW7OHEIrhi3HKnsd8EntmXaLpZisSDyJ2HLKOY,2038
|
|
8
8
|
karrio/providers/eshipper/metadata.json,sha256=KBae69ntmCIkZ0dKrAX0vjkm1CyE9UwvIstt7mLrq4M,369526
|
|
9
|
-
karrio/providers/eshipper/rate.py,sha256=
|
|
10
|
-
karrio/providers/eshipper/tracking.py,sha256=
|
|
11
|
-
karrio/providers/eshipper/units.py,sha256=
|
|
12
|
-
karrio/providers/eshipper/utils.py,sha256=
|
|
9
|
+
karrio/providers/eshipper/rate.py,sha256=sShKjW25Av07gPE3dIHEe4X2-Vw6bwtbmpIQzlLZJsE,7301
|
|
10
|
+
karrio/providers/eshipper/tracking.py,sha256=wIYItHY_aQ1Cnth6c9Wqdjfr4fn9fALTBiWSB-FhjLs,3137
|
|
11
|
+
karrio/providers/eshipper/units.py,sha256=rY8zN9DewnwcyFGcI_zsxdtq40MyIc9vBGM-mRSIzac,10563
|
|
12
|
+
karrio/providers/eshipper/utils.py,sha256=LOHTEHMgoLpYIPMuCRxcyGwVp3UsMC7DZ3h36YtXwvQ,403
|
|
13
13
|
karrio/providers/eshipper/shipment/__init__.py,sha256=oPCX3bykNDVeczan1tbmSpuOtFYTtd6h8J9otYVDQqQ,233
|
|
14
14
|
karrio/providers/eshipper/shipment/cancel.py,sha256=vdxMmK7AmI-xOn62NlaMzCCKQ8CBql8QBVPft0VQWn0,1317
|
|
15
|
-
karrio/providers/eshipper/shipment/create.py,sha256=
|
|
15
|
+
karrio/providers/eshipper/shipment/create.py,sha256=4Y_t011XNIaFXqEsuEahAp2kQviCVMYUgfI2ccn9BmM,12408
|
|
16
16
|
karrio/schemas/eshipper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
karrio/schemas/eshipper/cancel_request.py,sha256=mGMmVxCEwUhWyw-XkK4wsHZ4OQ34e6Dnc0JxBU1CixQ,334
|
|
18
18
|
karrio/schemas/eshipper/cancel_response.py,sha256=yLJe8qk71gy6GVGZ07ylBIomYFLUChZx9GDyJ3XzEsA,346
|
|
@@ -26,8 +26,8 @@ karrio/schemas/eshipper/shipping_request.py,sha256=OrqSutwyzTj-iau3LDPJUEGBRzzV9
|
|
|
26
26
|
karrio/schemas/eshipper/shipping_response.py,sha256=RdMXh9goyTrH2qdU_5KMDIAWAEDzMchy_e5f45k-zxs,3892
|
|
27
27
|
karrio/schemas/eshipper/tracking_request.py,sha256=EAE7PJFp2rTYCXMQJ6Kg-hMs3vL-GSTFjhHvzSmMzPg,252
|
|
28
28
|
karrio/schemas/eshipper/tracking_response.py,sha256=2m9doy3y5f6zdA-gbNfsw8rp0oqs3eYrgw8Por_1fjE,2219
|
|
29
|
-
karrio_eshipper-
|
|
30
|
-
karrio_eshipper-
|
|
31
|
-
karrio_eshipper-
|
|
32
|
-
karrio_eshipper-
|
|
33
|
-
karrio_eshipper-
|
|
29
|
+
karrio_eshipper-2026.1.dist-info/METADATA,sha256=gehsHjeLm7Fc7qE_7GZfYleIeaeMPydoIM8_eYeLllU,995
|
|
30
|
+
karrio_eshipper-2026.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
31
|
+
karrio_eshipper-2026.1.dist-info/entry_points.txt,sha256=u9RQVmPE2uC3-aL85DnlIHdsHAWYkOnwTAfJvi2--DY,61
|
|
32
|
+
karrio_eshipper-2026.1.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
|
|
33
|
+
karrio_eshipper-2026.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|