karrio-amazon-shipping 2025.5.5__py3-none-any.whl → 2025.5.7__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.
@@ -1,12 +1,80 @@
1
1
  import typing
2
+ import datetime
3
+ import urllib.parse
2
4
  import karrio.lib as lib
3
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.amazon_shipping.error as provider_error
4
9
  from karrio.mappers.amazon_shipping.settings import Settings
5
10
 
6
11
 
7
12
  class Proxy(proxy.Proxy):
8
13
  settings: Settings
9
14
 
15
+ def authenticate(self, _=None) -> lib.Deserializable[str]:
16
+ """Retrieve the access_token using the seller_id|developer_id pair
17
+ or collect it from the cache if an unexpired access_token exist.
18
+ """
19
+ cache_key = f"{self.settings.carrier_name}|{self.settings.seller_id}|{self.settings.developer_id}"
20
+
21
+ def get_token():
22
+ query = urllib.parse.urlencode(
23
+ dict(
24
+ developerId=self.settings.developer_id,
25
+ sellingPartnerId=self.settings.seller_id,
26
+ mwsAuthToken=self.settings.mws_auth_token,
27
+ )
28
+ )
29
+ result = lib.request(
30
+ url=f"{self.settings.server_url}/authorization/v1/authorizationCode?{query}",
31
+ trace=self.settings.trace_as("json"),
32
+ method="POST",
33
+ headers={"content-Type": "application/json"},
34
+ max_retries=2,
35
+ )
36
+
37
+ response = lib.to_dict(result)
38
+ messages = provider_error.parse_error_response(response, self.settings)
39
+
40
+ if any(messages):
41
+ raise errors.ParsedMessagesError(messages)
42
+
43
+ # Validate that authorizationCode is present in the response payload
44
+ authorization_code = lib.failsafe(
45
+ lambda: response.get("payload", {}).get("authorizationCode")
46
+ )
47
+ if not authorization_code:
48
+ raise errors.ParsedMessagesError(
49
+ messages=[
50
+ models.Message(
51
+ carrier_name=self.settings.carrier_name,
52
+ carrier_id=self.settings.carrier_id,
53
+ message="Authentication failed: No authorization code received",
54
+ code="AUTH_ERROR",
55
+ )
56
+ ]
57
+ )
58
+
59
+ expiry = datetime.datetime.now() + datetime.timedelta(
60
+ seconds=float(response.get("expires_in", 0))
61
+ )
62
+
63
+ return {
64
+ **response,
65
+ "expiry": lib.fdatetime(expiry),
66
+ "authorizationCode": authorization_code,
67
+ }
68
+
69
+ token = self.settings.connection_cache.thread_safe(
70
+ refresh_func=get_token,
71
+ cache_key=cache_key,
72
+ buffer_minutes=30,
73
+ token_field="authorizationCode",
74
+ )
75
+
76
+ return lib.Deserializable(token.get_state())
77
+
10
78
  def get_rates(self, request: lib.Serializable) -> lib.Deserializable:
11
79
  response = self._send_request(
12
80
  path="/shipping/v1/rates",
@@ -31,11 +99,18 @@ class Proxy(proxy.Proxy):
31
99
  return lib.Deserializable(response if any(response) else "{}", lib.to_dict)
32
100
 
33
101
  def get_tracking(self, request: lib.Serializable) -> lib.Deserializable:
102
+ access_token = self.authenticate().deserialize()
103
+
34
104
  track = lambda trackingId: (
35
105
  trackingId,
36
- self._send_request(
37
- path=f"/shipping/v1/tracking/{trackingId}",
106
+ lib.request(
107
+ url=f"{self.settings.server_url}/shipping/v1/tracking/{trackingId}",
108
+ trace=self.trace_as("json"),
38
109
  method="GET",
110
+ headers={
111
+ "Content-Type": "application/json",
112
+ "x-amz-access-token": access_token,
113
+ },
39
114
  ),
40
115
  )
41
116
 
@@ -50,6 +125,7 @@ class Proxy(proxy.Proxy):
50
125
  def _send_request(
51
126
  self, path: str, request: lib.Serializable = None, method: str = "POST"
52
127
  ) -> str:
128
+ access_token = self.authenticate().deserialize()
53
129
  data: dict = dict(data=request.serialize()) if request is not None else dict()
54
130
  return lib.request(
55
131
  **{
@@ -58,7 +134,7 @@ class Proxy(proxy.Proxy):
58
134
  "method": method,
59
135
  "headers": {
60
136
  "Content-Type": "application/json",
61
- "x-amz-access-token": self.settings.access_token,
137
+ "x-amz-access-token": access_token,
62
138
  },
63
139
  **data,
64
140
  }
@@ -4,6 +4,7 @@ import karrio.lib as lib
4
4
  import karrio.core.models as models
5
5
  import karrio.providers.amazon_shipping.error as error
6
6
  import karrio.providers.amazon_shipping.utils as provider_utils
7
+ import karrio.providers.amazon_shipping.units as provider_units
7
8
 
8
9
 
9
10
  def parse_tracking_response(
@@ -56,6 +57,23 @@ def _extract_details(
56
57
  join=True,
57
58
  separator=", ",
58
59
  ),
60
+ timestamp=lib.fiso_timestamp(event.eventTime, current_format="%Y-%m-%dT%H:%M:%SZ"),
61
+ status=next(
62
+ (
63
+ s.name
64
+ for s in list(provider_units.TrackingStatus)
65
+ if event.eventCode in s.value
66
+ ),
67
+ None,
68
+ ),
69
+ reason=next(
70
+ (
71
+ r.name
72
+ for r in list(provider_units.TrackingIncidentReason)
73
+ if event.eventCode in r.value
74
+ ),
75
+ None,
76
+ ),
59
77
  )
60
78
  for event in details.eventHistory
61
79
  ],
@@ -18,3 +18,19 @@ class Service(lib.StrEnum):
18
18
  amazon_shipping_ground = "Amazon Shipping Ground"
19
19
  amazon_shipping_standard = "Amazon Shipping Standard"
20
20
  amazon_shipping_premium = "Amazon Shipping Premium"
21
+
22
+
23
+ class TrackingStatus(lib.Enum):
24
+ on_hold = []
25
+ delivered = ["Delivered"]
26
+ in_transit = []
27
+ delivery_failed = []
28
+ out_for_delivery = []
29
+
30
+
31
+ class TrackingIncidentReason(lib.Enum):
32
+ """Maps Amazon Shipping exception codes to normalized TrackingIncidentReason."""
33
+ carrier_damaged_parcel = []
34
+ consignee_refused = []
35
+ consignee_not_home = []
36
+ unknown = []
@@ -1,9 +1,6 @@
1
- import datetime
2
- import urllib.parse
3
- import karrio.lib as lib
1
+ """Amazon Shipping connection settings."""
2
+
4
3
  import karrio.core as core
5
- import karrio.core.errors as errors
6
- import karrio.core.models as models
7
4
 
8
5
 
9
6
  class Settings(core.Settings):
@@ -38,66 +35,3 @@ class Settings(core.Settings):
38
35
  @property
39
36
  def carrier_name(self):
40
37
  return "amazon_shipping"
41
-
42
- @property
43
- def access_token(self):
44
- """Retrieve the access_token using the seller_id|developer_id pair
45
- or collect it from the cache if an unexpired access_token exist.
46
- """
47
- cache_key = f"{self.carrier_name}|{self.seller_id}|{self.developer_id}"
48
-
49
- return self.connection_cache.thread_safe(
50
- refresh_func=lambda: login(self),
51
- cache_key=cache_key,
52
- buffer_minutes=30,
53
- token_field="authorizationCode",
54
- ).get_state()
55
-
56
-
57
- def login(settings: Settings):
58
- import karrio.providers.amazon_shipping.error as error
59
-
60
- query = urllib.parse.urlencode(
61
- dict(
62
- developerId=settings.developer_id,
63
- sellingPartnerId=settings.seller_id,
64
- mwsAuthToken=settings.mws_auth_token,
65
- )
66
- )
67
- result = lib.request(
68
- url=f"{settings.server_url}/authorization/v1/authorizationCode?{query}",
69
- headers={"content-Type": "application/json"},
70
- method="POST",
71
- )
72
-
73
- response = lib.to_dict(result)
74
- messages = error.parse_error_response(response, settings)
75
-
76
- if any(messages):
77
- raise errors.ParsedMessagesError(messages)
78
-
79
- # Validate that authorizationCode is present in the response payload
80
- authorization_code = lib.failsafe(
81
- lambda: response.get("payload", {}).get("authorizationCode")
82
- )
83
- if not authorization_code:
84
- raise errors.ParsedMessagesError(
85
- messages=[
86
- models.Message(
87
- carrier_name=settings.carrier_name,
88
- carrier_id=settings.carrier_id,
89
- message="Authentication failed: No authorization code received",
90
- code="AUTH_ERROR",
91
- )
92
- ]
93
- )
94
-
95
- expiry = datetime.datetime.now() + datetime.timedelta(
96
- seconds=float(response.get("expires_in", 0))
97
- )
98
-
99
- return {
100
- **response,
101
- "expiry": lib.fdatetime(expiry),
102
- "authorizationCode": authorization_code,
103
- }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karrio_amazon_shipping
3
- Version: 2025.5.5
3
+ Version: 2025.5.7
4
4
  Summary: Karrio - Amazon Shipping Extension
5
5
  Author-email: karrio <hello@karrio.io>
6
6
  License-Expression: LGPL-3.0
@@ -1,14 +1,14 @@
1
1
  karrio/mappers/amazon_shipping/__init__.py,sha256=a-I2ruFujHDDyd7cA8vAPPom9-WjxhxTg8yeoqy5I1c,173
2
2
  karrio/mappers/amazon_shipping/mapper.py,sha256=MHRzpj3yt929OuZtGgyfJ1WByq951lMDFG7b5CUoyPY,2122
3
- karrio/mappers/amazon_shipping/proxy.py,sha256=9heU65ELMqct9_Mf2hTYvOxJ6MPW1cZv0NAsYIVWz7o,2230
3
+ karrio/mappers/amazon_shipping/proxy.py,sha256=6ro1kpmHq7ugeuPRKN-PU1pJetqZFXbIYbSHxTMfctg,5204
4
4
  karrio/mappers/amazon_shipping/settings.py,sha256=v_d5kYrKA5Wvv42TgOhvxayHeY4bHGrDRldMcpUkUf4,601
5
5
  karrio/plugins/amazon_shipping/__init__.py,sha256=DM1Sxy3lvH6sWMktRgfRI-ned-tgN8tK46PkaV92EqM,432
6
6
  karrio/providers/amazon_shipping/__init__.py,sha256=bccHAUMrkWlBQQq9WEZv3os_STMF-JPsO1w01NNUzxk,367
7
7
  karrio/providers/amazon_shipping/error.py,sha256=khbjAf7thICwU71ej2n2PgvYEJaUt2BHl9G-Z1fHEMc,901
8
8
  karrio/providers/amazon_shipping/rate.py,sha256=7BS9Mn5-eUfVpZkQ0X1fCEoKDpHIuJO3P_t-s1Mn7kY,3625
9
- karrio/providers/amazon_shipping/tracking.py,sha256=wOZt98U4RenEhX6C0CioJDNXRE986_sFt9_zsfnYdGs,2294
10
- karrio/providers/amazon_shipping/units.py,sha256=DKT9sHUVfTWD1d46pu68TOfunajj4lGXejKPQIOxWyA,486
11
- karrio/providers/amazon_shipping/utils.py,sha256=fFU-p-JloDBeiB0JVb_NJuIye6WcvMF4QbSjCSyW5Ys,3135
9
+ karrio/providers/amazon_shipping/tracking.py,sha256=YEfneC834IDQwqvNd3EL-fhipDcNtKptb_MVaN7wfjY,3012
10
+ karrio/providers/amazon_shipping/units.py,sha256=9uE4Dum9kPMyGNS6vEdgUvT9Tt1UlIHTDtNs-5zzo_g,869
11
+ karrio/providers/amazon_shipping/utils.py,sha256=wMeeXDkJRDhyWxtZ3RCxkvgvy-wBEafoFhaFVokCETk,1029
12
12
  karrio/providers/amazon_shipping/shipment/__init__.py,sha256=ZD_QkyZjlp4VThWp5ebDfavYPHTy6lvOJCXmMEUtFZ8,246
13
13
  karrio/providers/amazon_shipping/shipment/cancel.py,sha256=Z4JkHPn1tJO8dOiEEf9W47naJXf4W3CKmfLUzxY9S1U,985
14
14
  karrio/providers/amazon_shipping/shipment/create.py,sha256=X1CHwOCIcwUg-HDsk38-0XnpYahPAqL8zfMF11cb_LI,4374
@@ -24,8 +24,8 @@ karrio/schemas/amazon_shipping/rate_request.py,sha256=sc0mAjuS2lY_uqQA4Je7Sj59Vb
24
24
  karrio/schemas/amazon_shipping/rate_response.py,sha256=P6xypSrTnnrWL0IGWinRY1ZCytKi-c2aqWD9o9C-QuY,938
25
25
  karrio/schemas/amazon_shipping/shipping_label.py,sha256=8aLQpIFVv7dPdknjN8S-053zhA9gv2JpVi2F8prGMWo,382
26
26
  karrio/schemas/amazon_shipping/tracking_response.py,sha256=RQS_mAYgr0m_BHeEX4jbz4NxB1Kj7SX5_jHGXKLwOcc,854
27
- karrio_amazon_shipping-2025.5.5.dist-info/METADATA,sha256=i-O3bMt-yL9LV_fJdkwKAOxE8GfKuQ9JNwcnA-vpen8,1044
28
- karrio_amazon_shipping-2025.5.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- karrio_amazon_shipping-2025.5.5.dist-info/entry_points.txt,sha256=jwZSXX8ySMgVsTaxzrEf4UMGMDTUAMRcBYLjqTOQnWc,75
30
- karrio_amazon_shipping-2025.5.5.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
31
- karrio_amazon_shipping-2025.5.5.dist-info/RECORD,,
27
+ karrio_amazon_shipping-2025.5.7.dist-info/METADATA,sha256=6hKmlTw-w-FnsPYL9tQ1FgpnAXuxtXHT_pQAlQRVFqE,1044
28
+ karrio_amazon_shipping-2025.5.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ karrio_amazon_shipping-2025.5.7.dist-info/entry_points.txt,sha256=jwZSXX8ySMgVsTaxzrEf4UMGMDTUAMRcBYLjqTOQnWc,75
30
+ karrio_amazon_shipping-2025.5.7.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
31
+ karrio_amazon_shipping-2025.5.7.dist-info/RECORD,,