karrio-easyship 2025.5rc1__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.
Files changed (39) hide show
  1. karrio/mappers/easyship/__init__.py +3 -0
  2. karrio/mappers/easyship/mapper.py +94 -0
  3. karrio/mappers/easyship/proxy.py +155 -0
  4. karrio/mappers/easyship/settings.py +20 -0
  5. karrio/plugins/easyship/__init__.py +20 -0
  6. karrio/providers/easyship/__init__.py +25 -0
  7. karrio/providers/easyship/error.py +38 -0
  8. karrio/providers/easyship/manifest.py +72 -0
  9. karrio/providers/easyship/metadata.json +6985 -0
  10. karrio/providers/easyship/pickup/__init__.py +4 -0
  11. karrio/providers/easyship/pickup/cancel.py +42 -0
  12. karrio/providers/easyship/pickup/create.py +86 -0
  13. karrio/providers/easyship/pickup/update.py +88 -0
  14. karrio/providers/easyship/rate.py +215 -0
  15. karrio/providers/easyship/shipment/__init__.py +9 -0
  16. karrio/providers/easyship/shipment/cancel.py +39 -0
  17. karrio/providers/easyship/shipment/create.py +306 -0
  18. karrio/providers/easyship/tracking.py +110 -0
  19. karrio/providers/easyship/units.py +162 -0
  20. karrio/providers/easyship/utils.py +98 -0
  21. karrio/schemas/easyship/__init__.py +0 -0
  22. karrio/schemas/easyship/error_response.py +17 -0
  23. karrio/schemas/easyship/manifest_request.py +9 -0
  24. karrio/schemas/easyship/manifest_response.py +31 -0
  25. karrio/schemas/easyship/pickup_cancel_response.py +19 -0
  26. karrio/schemas/easyship/pickup_request.py +13 -0
  27. karrio/schemas/easyship/pickup_response.py +85 -0
  28. karrio/schemas/easyship/rate_request.py +100 -0
  29. karrio/schemas/easyship/rate_response.py +124 -0
  30. karrio/schemas/easyship/shipment_cancel_response.py +19 -0
  31. karrio/schemas/easyship/shipment_request.py +147 -0
  32. karrio/schemas/easyship/shipment_response.py +273 -0
  33. karrio/schemas/easyship/tracking_request.py +49 -0
  34. karrio/schemas/easyship/tracking_response.py +54 -0
  35. karrio_easyship-2025.5rc1.dist-info/METADATA +45 -0
  36. karrio_easyship-2025.5rc1.dist-info/RECORD +39 -0
  37. karrio_easyship-2025.5rc1.dist-info/WHEEL +5 -0
  38. karrio_easyship-2025.5rc1.dist-info/entry_points.txt +2 -0
  39. karrio_easyship-2025.5rc1.dist-info/top_level.txt +3 -0
@@ -0,0 +1,3 @@
1
+ from karrio.mappers.easyship.mapper import Mapper
2
+ from karrio.mappers.easyship.proxy import Proxy
3
+ from karrio.mappers.easyship.settings import Settings
@@ -0,0 +1,94 @@
1
+ """Karrio Easyship client mapper."""
2
+
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.api.mapper as mapper
6
+ import karrio.core.models as models
7
+ import karrio.providers.easyship as provider
8
+ import karrio.mappers.easyship.settings as provider_settings
9
+
10
+
11
+ class Mapper(mapper.Mapper):
12
+ settings: provider_settings.Settings
13
+
14
+ def create_rate_request(
15
+ self, payload: models.RateRequest
16
+ ) -> lib.Serializable:
17
+ return provider.rate_request(payload, self.settings)
18
+
19
+ def create_tracking_request(
20
+ self, payload: models.TrackingRequest
21
+ ) -> lib.Serializable:
22
+ return provider.tracking_request(payload, self.settings)
23
+
24
+ def create_shipment_request(
25
+ self, payload: models.ShipmentRequest
26
+ ) -> lib.Serializable:
27
+ return provider.shipment_request(payload, self.settings)
28
+
29
+ def create_pickup_request(
30
+ self, payload: models.PickupRequest
31
+ ) -> lib.Serializable:
32
+ return provider.pickup_request(payload, self.settings)
33
+
34
+ def create_pickup_update_request(
35
+ self, payload: models.PickupUpdateRequest
36
+ ) -> lib.Serializable:
37
+ return provider.pickup_update_request(payload, self.settings)
38
+
39
+ def create_cancel_pickup_request(
40
+ self, payload: models.PickupCancelRequest
41
+ ) -> lib.Serializable:
42
+ return provider.pickup_cancel_request(payload, self.settings)
43
+
44
+ def create_cancel_shipment_request(
45
+ self, payload: models.ShipmentCancelRequest
46
+ ) -> lib.Serializable[str]:
47
+ return provider.shipment_cancel_request(payload, self.settings)
48
+
49
+ def create_manifest_request(
50
+ self, payload: models.ManifestRequest
51
+ ) -> lib.Serializable:
52
+ return provider.manifest_request(payload, self.settings)
53
+
54
+
55
+ def parse_cancel_pickup_response(
56
+ self, response: lib.Deserializable[str]
57
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
58
+ return provider.parse_pickup_cancel_response(response, self.settings)
59
+
60
+ def parse_cancel_shipment_response(
61
+ self, response: lib.Deserializable[str]
62
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
63
+ return provider.parse_shipment_cancel_response(response, self.settings)
64
+
65
+ def parse_pickup_response(
66
+ self, response: lib.Deserializable[str]
67
+ ) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
68
+ return provider.parse_pickup_response(response, self.settings)
69
+
70
+ def parse_pickup_update_response(
71
+ self, response: lib.Deserializable[str]
72
+ ) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
73
+ return provider.parse_pickup_update_response(response, self.settings)
74
+
75
+ def parse_rate_response(
76
+ self, response: lib.Deserializable[str]
77
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
78
+ return provider.parse_rate_response(response, self.settings)
79
+
80
+ def parse_shipment_response(
81
+ self, response: lib.Deserializable[str]
82
+ ) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
83
+ return provider.parse_shipment_response(response, self.settings)
84
+
85
+ def parse_tracking_response(
86
+ self, response: lib.Deserializable[str]
87
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
88
+ return provider.parse_tracking_response(response, self.settings)
89
+
90
+ def parse_manifest_response(
91
+ self, response: lib.Deserializable[str]
92
+ ) -> typing.Tuple[models.ManifestDetails, typing.List[models.Message]]:
93
+ return provider.parse_manifest_response(response, self.settings)
94
+
@@ -0,0 +1,155 @@
1
+ """Karrio Easyship client proxy."""
2
+
3
+ import karrio.lib as lib
4
+ import karrio.api.proxy as proxy
5
+ import karrio.mappers.easyship.settings as provider_settings
6
+
7
+
8
+ class Proxy(proxy.Proxy):
9
+ settings: provider_settings.Settings
10
+
11
+ def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
12
+ response = lib.request(
13
+ url=f"{self.settings.server_url}/2023-01/rates",
14
+ data=lib.to_json(request.serialize()),
15
+ trace=self.trace_as("json"),
16
+ method="POST",
17
+ headers={
18
+ "Authorization": f"Bearer {self.settings.access_token}",
19
+ "Content-Type": "application/json",
20
+ "user-agent": "app/1.0",
21
+ },
22
+ )
23
+
24
+ return lib.Deserializable(response, lib.to_dict)
25
+
26
+ def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
27
+ # create shipment
28
+ response = lib.request(
29
+ url=f"{self.settings.server_url}/2023-01/shipments",
30
+ data=lib.to_json(request.serialize()),
31
+ trace=self.trace_as("json"),
32
+ method="POST",
33
+ headers={
34
+ "Authorization": f"Bearer {self.settings.access_token}",
35
+ "Content-Type": "application/json",
36
+ "user-agent": "app/1.0",
37
+ },
38
+ )
39
+
40
+ return lib.Deserializable(response, lib.to_dict, request.ctx)
41
+
42
+ def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
43
+ easyship_shipment_id = request.serialize().get("easyship_shipment_id")
44
+ response = lib.request(
45
+ url=f"{self.settings.server_url}/2023-01/shipments/{easyship_shipment_id}/cancel",
46
+ trace=self.trace_as("json"),
47
+ method="POST",
48
+ headers={
49
+ "Authorization": f"Bearer {self.settings.access_token}",
50
+ "Content-Type": "application/json",
51
+ "user-agent": "app/1.0",
52
+ },
53
+ )
54
+
55
+ return lib.Deserializable(response, lib.to_dict)
56
+
57
+ def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
58
+ responses = lib.run_asynchronously(
59
+ lambda data: (
60
+ data["shipment_id"],
61
+ lib.request(
62
+ url=f"{self.settings.server_url}/2023-01/shipments/{data['shipment_id']}",
63
+ trace=self.trace_as("json"),
64
+ method="GET",
65
+ headers={
66
+ "Authorization": f"Bearer {self.settings.access_token}",
67
+ "Content-Type": "application/json",
68
+ "user-agent": "app/1.0",
69
+ },
70
+ ),
71
+ ),
72
+ [_ for _ in request.serialize() if _.get("shipment_id")],
73
+ )
74
+
75
+ return lib.Deserializable(
76
+ responses,
77
+ lambda __: [(_[0], lib.to_dict(_[1])) for _ in __],
78
+ )
79
+
80
+ def schedule_pickup(self, request: lib.Serializable) -> lib.Deserializable[str]:
81
+ response = lib.request(
82
+ url=f"{self.settings.server_url}/2023-01/pickups",
83
+ data=lib.to_json(request.serialize()),
84
+ trace=self.trace_as("json"),
85
+ method="POST",
86
+ headers={
87
+ "Authorization": f"Bearer {self.settings.access_token}",
88
+ "Content-Type": "application/json",
89
+ "user-agent": "app/1.0",
90
+ },
91
+ )
92
+
93
+ return lib.Deserializable(response, lib.to_dict)
94
+
95
+ def modify_pickup(self, request: lib.Serializable) -> lib.Deserializable[str]:
96
+ response = self.cancel_pickup(lib.Serializable(request.ctx))
97
+
98
+ if response.deserialize() is not None:
99
+ response = self.schedule_pickup(request)
100
+
101
+ return lib.Deserializable(response, lib.to_dict, request.ctx)
102
+
103
+ def cancel_pickup(self, request: lib.Serializable) -> lib.Deserializable[str]:
104
+ easyship_pickup_id = request.serialize().get("easyship_pickup_id")
105
+ response = lib.request(
106
+ url=f"{self.settings.server_url}/2023-01/pickups/{easyship_pickup_id}/cancel",
107
+ trace=self.trace_as("json"),
108
+ method="POST",
109
+ headers={
110
+ "Authorization": f"Bearer {self.settings.access_token}",
111
+ "Content-Type": "application/json",
112
+ "user-agent": "app/1.0",
113
+ },
114
+ )
115
+
116
+ return lib.Deserializable(response, lib.to_dict)
117
+
118
+ def create_manifest(self, request: lib.Serializable) -> lib.Deserializable[str]:
119
+ # create manifest
120
+ response = lib.to_dict(
121
+ lib.request(
122
+ url=f"{self.settings.server_url}/2023-01/manifests",
123
+ data=lib.to_json(request.serialize()),
124
+ trace=self.trace_as("json"),
125
+ method="POST",
126
+ headers={
127
+ "Authorization": f"Bearer {self.settings.access_token}",
128
+ "Content-Type": "application/json",
129
+ "user-agent": "app/1.0",
130
+ },
131
+ )
132
+ )
133
+
134
+ # extract manifest url
135
+ manifest_url = lib.failsafe(lambda: response["manifest"]["document"]["url"])
136
+
137
+ # download manifest file
138
+ response.update(
139
+ manifest_file=lib.identity(
140
+ None
141
+ if manifest_url is None
142
+ else lib.request(
143
+ url=manifest_url,
144
+ method="GET",
145
+ headers={
146
+ "Authorization": f"Bearer {self.settings.access_token}",
147
+ "origin": "http://localhost:5002",
148
+ "user-agent": "app/1.0",
149
+ },
150
+ decoder=lib.encode_base64,
151
+ )
152
+ )
153
+ )
154
+
155
+ return lib.Deserializable(response)
@@ -0,0 +1,20 @@
1
+ """Karrio Easyship client settings."""
2
+
3
+ import attr
4
+ import karrio.providers.easyship.utils as provider_utils
5
+
6
+
7
+ @attr.s(auto_attribs=True)
8
+ class Settings(provider_utils.Settings):
9
+ """Easyship connection settings."""
10
+
11
+ # Add carrier specific API connection properties here
12
+ access_token: str
13
+
14
+ # generic properties
15
+ id: str = None
16
+ test_mode: bool = False
17
+ carrier_id: str = "easyship"
18
+ account_country_code: str = None
19
+ metadata: dict = {}
20
+ config: dict = {}
@@ -0,0 +1,20 @@
1
+ import karrio.core.metadata as metadata
2
+ import karrio.mappers.easyship as mappers
3
+ import karrio.providers.easyship.units as units
4
+ import karrio.providers.easyship.utils as utils
5
+
6
+
7
+ METADATA = metadata.PluginMetadata(
8
+ status="production-ready",
9
+ id="easyship",
10
+ label="Easyship",
11
+ # Integrations
12
+ Mapper=mappers.Mapper,
13
+ Proxy=mappers.Proxy,
14
+ Settings=mappers.Settings,
15
+ # Data Units
16
+ is_hub=True,
17
+ options=units.ShippingOption,
18
+ services=units.ShippingService,
19
+ connection_configs=utils.ConnectionConfig,
20
+ )
@@ -0,0 +1,25 @@
1
+ """Karrio Easyship provider imports."""
2
+ from karrio.providers.easyship.utils import Settings
3
+ from karrio.providers.easyship.rate import parse_rate_response, rate_request
4
+ from karrio.providers.easyship.shipment import (
5
+ parse_shipment_cancel_response,
6
+ parse_shipment_response,
7
+ shipment_cancel_request,
8
+ shipment_request,
9
+ )
10
+ from karrio.providers.easyship.pickup import (
11
+ parse_pickup_cancel_response,
12
+ parse_pickup_update_response,
13
+ parse_pickup_response,
14
+ pickup_update_request,
15
+ pickup_cancel_request,
16
+ pickup_request,
17
+ )
18
+ from karrio.providers.easyship.tracking import (
19
+ parse_tracking_response,
20
+ tracking_request,
21
+ )
22
+ from karrio.providers.easyship.manifest import (
23
+ parse_manifest_response,
24
+ manifest_request,
25
+ )
@@ -0,0 +1,38 @@
1
+ """Karrio Easyship error parser."""
2
+
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.models as models
6
+ import karrio.providers.easyship.utils as provider_utils
7
+
8
+
9
+ def parse_error_response(
10
+ response: dict,
11
+ settings: provider_utils.Settings,
12
+ **kwargs,
13
+ ) -> typing.List[models.Message]:
14
+ errors: list = [
15
+ *([response["error"]] if response.get("error") else []),
16
+ *[
17
+ dict(code="warning", message=message)
18
+ for message in response.get("meta", {}).get("errors", [])
19
+ ],
20
+ ]
21
+
22
+ return [
23
+ models.Message(
24
+ carrier_id=settings.carrier_id,
25
+ carrier_name=settings.carrier_name,
26
+ code=error.get("code"),
27
+ message=error.get("message"),
28
+ details=lib.to_dict(
29
+ {
30
+ **kwargs,
31
+ "details": error.get("details"),
32
+ "request_id": error.get("request_id"),
33
+ "type": error.get("type"),
34
+ }
35
+ ),
36
+ )
37
+ for error in errors
38
+ ]
@@ -0,0 +1,72 @@
1
+ """Karrio Easyship manifest API implementation."""
2
+
3
+ import karrio.schemas.easyship.manifest_request as easyship
4
+ import karrio.schemas.easyship.manifest_response as manifests
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.models as models
9
+ import karrio.providers.easyship.error as error
10
+ import karrio.providers.easyship.utils as provider_utils
11
+ import karrio.providers.easyship.units as provider_units
12
+
13
+
14
+ def parse_manifest_response(
15
+ _response: lib.Deserializable[dict],
16
+ settings: provider_utils.Settings,
17
+ ) -> typing.Tuple[models.ManifestDetails, typing.List[models.Message]]:
18
+ response = _response.deserialize()
19
+
20
+ messages = error.parse_error_response(response, settings)
21
+ details = lib.identity(
22
+ _extract_details(response, settings)
23
+ if response.get("manifest") is not None
24
+ else None
25
+ )
26
+
27
+ return details, messages
28
+
29
+
30
+ def _extract_details(
31
+ data: dict,
32
+ settings: provider_utils.Settings,
33
+ ) -> models.ManifestDetails:
34
+ details = lib.to_object(manifests.ManifestType, data["manifest"])
35
+
36
+ return models.ManifestDetails(
37
+ carrier_id=settings.carrier_id,
38
+ carrier_name=settings.carrier_id,
39
+ doc=models.ManifestDocument(manifest=data["manifest_file"]),
40
+ meta=dict(
41
+ courier_umbrella_name=details.courier_umbrella_name,
42
+ courier_account_id=details.courier_account_id,
43
+ manifest_url=details.document.url,
44
+ reference=details.ref_number,
45
+ manifest_id=details.id,
46
+ ),
47
+ )
48
+
49
+
50
+ def manifest_request(
51
+ payload: models.ManifestRequest,
52
+ settings: provider_utils.Settings,
53
+ ) -> lib.Serializable:
54
+ options = lib.units.Options(
55
+ payload.options,
56
+ option_type=lib.units.create_enum(
57
+ "ManifestOptions",
58
+ {
59
+ "easyship_courier_account_id": lib.OptionEnum(
60
+ "easyship_courier_account_id"
61
+ ),
62
+ },
63
+ ),
64
+ )
65
+
66
+ # map data to convert karrio model to easyship specific type
67
+ request = easyship.ManifestRequestType(
68
+ courier_account_id=options.easyship_courier_account_id.state,
69
+ shipment_ids=payload.shipment_identifiers,
70
+ )
71
+
72
+ return lib.Serializable(request, lib.to_dict)