karrio-cli 2025.5rc3__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_cli/__init__.py +0 -0
- karrio_cli/__main__.py +105 -0
- karrio_cli/ai/README.md +335 -0
- karrio_cli/ai/__init__.py +0 -0
- karrio_cli/ai/commands.py +102 -0
- karrio_cli/ai/karrio_ai/__init__.py +1 -0
- karrio_cli/ai/karrio_ai/agent.py +972 -0
- karrio_cli/ai/karrio_ai/architecture/INTEGRATION_AGENT_PROMPT.md +497 -0
- karrio_cli/ai/karrio_ai/architecture/MAPPING_AGENT_PROMPT.md +355 -0
- karrio_cli/ai/karrio_ai/architecture/REAL_WORLD_TESTING.md +305 -0
- karrio_cli/ai/karrio_ai/architecture/SCHEMA_AGENT_PROMPT.md +183 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_AGENT_PROMPT.md +448 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_GUIDE.md +271 -0
- karrio_cli/ai/karrio_ai/enhanced_tools.py +943 -0
- karrio_cli/ai/karrio_ai/rag_system.py +503 -0
- karrio_cli/ai/karrio_ai/tests/test_agent.py +350 -0
- karrio_cli/ai/karrio_ai/tests/test_real_integration.py +360 -0
- karrio_cli/ai/karrio_ai/tests/test_real_world_scenarios.py +513 -0
- karrio_cli/commands/__init__.py +0 -0
- karrio_cli/commands/codegen.py +336 -0
- karrio_cli/commands/login.py +139 -0
- karrio_cli/commands/plugins.py +168 -0
- karrio_cli/commands/sdk.py +870 -0
- karrio_cli/common/queries.py +101 -0
- karrio_cli/common/utils.py +368 -0
- karrio_cli/resources/__init__.py +0 -0
- karrio_cli/resources/carriers.py +91 -0
- karrio_cli/resources/connections.py +207 -0
- karrio_cli/resources/events.py +151 -0
- karrio_cli/resources/logs.py +151 -0
- karrio_cli/resources/orders.py +144 -0
- karrio_cli/resources/shipments.py +210 -0
- karrio_cli/resources/trackers.py +287 -0
- karrio_cli/templates/__init__.py +9 -0
- karrio_cli/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-312.pyc +0 -0
- karrio_cli/templates/address.py +308 -0
- karrio_cli/templates/docs.py +150 -0
- karrio_cli/templates/documents.py +428 -0
- karrio_cli/templates/manifest.py +396 -0
- karrio_cli/templates/pickup.py +839 -0
- karrio_cli/templates/rates.py +638 -0
- karrio_cli/templates/sdk.py +947 -0
- karrio_cli/templates/shipments.py +892 -0
- karrio_cli/templates/tracking.py +437 -0
- karrio_cli-2025.5rc3.dist-info/METADATA +165 -0
- karrio_cli-2025.5rc3.dist-info/RECORD +68 -0
- karrio_cli-2025.5rc3.dist-info/WHEEL +5 -0
- karrio_cli-2025.5rc3.dist-info/entry_points.txt +2 -0
- karrio_cli-2025.5rc3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,839 @@
|
|
1
|
+
from jinja2 import Template
|
2
|
+
|
3
|
+
PROVIDER_PICKUP_CREATE_TEMPLATE = Template(
|
4
|
+
'''"""Karrio {{name}} pickup API implementation."""
|
5
|
+
|
6
|
+
import typing
|
7
|
+
import karrio.lib as lib
|
8
|
+
import karrio.core.models as models
|
9
|
+
import karrio.providers.{{id}}.error as error
|
10
|
+
import karrio.providers.{{id}}.utils as provider_utils
|
11
|
+
|
12
|
+
|
13
|
+
def parse_pickup_response(
|
14
|
+
_response: lib.Deserializable[{% if is_xml_api %}lib.Element{% else %}dict{% endif %}],
|
15
|
+
settings: provider_utils.Settings,
|
16
|
+
) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
|
17
|
+
"""
|
18
|
+
Parse pickup response from carrier API
|
19
|
+
|
20
|
+
_response: The carrier response to deserialize
|
21
|
+
settings: The carrier connection settings
|
22
|
+
|
23
|
+
Returns a tuple with (PickupDetails, List[Message])
|
24
|
+
"""
|
25
|
+
response = _response.deserialize()
|
26
|
+
messages = error.parse_error_response(response, settings)
|
27
|
+
|
28
|
+
# Extract pickup details
|
29
|
+
pickup = _extract_details(response, settings)
|
30
|
+
|
31
|
+
return pickup, messages
|
32
|
+
|
33
|
+
|
34
|
+
def _extract_details(
|
35
|
+
response: {% if is_xml_api %}lib.Element{% else %}dict{% endif %},
|
36
|
+
settings: provider_utils.Settings,
|
37
|
+
) -> models.PickupDetails:
|
38
|
+
"""
|
39
|
+
Extract pickup details from carrier response data
|
40
|
+
|
41
|
+
data: The carrier-specific pickup response data
|
42
|
+
settings: The carrier connection settings
|
43
|
+
|
44
|
+
Returns a PickupDetails object with the pickup information
|
45
|
+
"""
|
46
|
+
{% if is_xml_api %}
|
47
|
+
# Example implementation for XML response:
|
48
|
+
# Extract pickup details from the XML response
|
49
|
+
# confirmation_number = lib.find_element("confirmation-number", response, first=True).text
|
50
|
+
# pickup_date = lib.find_element("pickup-date", response, first=True).text
|
51
|
+
# ready_time = lib.find_element("ready-time", response, first=True).text
|
52
|
+
# closing_time = lib.find_element("closing-time", response, first=True).text
|
53
|
+
|
54
|
+
# For development, return sample data
|
55
|
+
confirmation_number = "PICKUP123"
|
56
|
+
pickup_date = lib.today_str()
|
57
|
+
ready_time = "09:00"
|
58
|
+
closing_time = "17:00"
|
59
|
+
{% else %}
|
60
|
+
# Example implementation for JSON response:
|
61
|
+
# Extract pickup details from the JSON response
|
62
|
+
# confirmation_number = response.get("confirmationNumber")
|
63
|
+
# pickup_date = response.get("pickupDate")
|
64
|
+
# ready_time = response.get("readyTime")
|
65
|
+
# closing_time = response.get("closingTime")
|
66
|
+
|
67
|
+
# For development, return sample data
|
68
|
+
confirmation_number = "PICKUP123"
|
69
|
+
pickup_date = lib.today_str()
|
70
|
+
ready_time = "09:00"
|
71
|
+
closing_time = "17:00"
|
72
|
+
{% endif %}
|
73
|
+
|
74
|
+
return models.PickupDetails(
|
75
|
+
carrier_id=settings.carrier_id,
|
76
|
+
carrier_name=settings.carrier_name,
|
77
|
+
confirmation_number=confirmation_number,
|
78
|
+
pickup_date=lib.fdate(pickup_date),
|
79
|
+
ready_time=ready_time,
|
80
|
+
closing_time=closing_time,
|
81
|
+
)
|
82
|
+
|
83
|
+
|
84
|
+
def pickup_request(
|
85
|
+
payload: models.PickupRequest,
|
86
|
+
settings: provider_utils.Settings,
|
87
|
+
) -> lib.Serializable:
|
88
|
+
"""
|
89
|
+
Create a pickup request for the carrier API
|
90
|
+
|
91
|
+
payload: The standardized PickupRequest from karrio
|
92
|
+
settings: The carrier connection settings
|
93
|
+
|
94
|
+
Returns a Serializable object that can be sent to the carrier API
|
95
|
+
"""
|
96
|
+
# Extract pickup details
|
97
|
+
address = lib.to_address(payload.address)
|
98
|
+
pickup_date = payload.pickup_date or lib.today_str()
|
99
|
+
ready_time = payload.ready_time or "09:00"
|
100
|
+
closing_time = payload.closing_time or "17:00"
|
101
|
+
|
102
|
+
{% if is_xml_api %}
|
103
|
+
# Example implementation for XML request:
|
104
|
+
request = f"""<?xml version="1.0"?>
|
105
|
+
<pickup-request>
|
106
|
+
<pickup-date>{pickup_date}</pickup-date>
|
107
|
+
<ready-time>{ready_time}</ready-time>
|
108
|
+
<closing-time>{closing_time}</closing-time>
|
109
|
+
<address>
|
110
|
+
<address-line1>{address.address_line1}</address-line1>
|
111
|
+
<city>{address.city}</city>
|
112
|
+
<postal-code>{address.postal_code}</postal-code>
|
113
|
+
<country-code>{address.country_code}</country-code>
|
114
|
+
<state-code>{address.state_code}</state-code>
|
115
|
+
<person-name>{address.person_name}</person-name>
|
116
|
+
<company-name>{address.company_name}</company-name>
|
117
|
+
<phone-number>{address.phone_number}</phone-number>
|
118
|
+
<email>{address.email}</email>
|
119
|
+
</address>
|
120
|
+
</pickup-request>"""
|
121
|
+
|
122
|
+
return lib.Serializable(request, lambda r: r)
|
123
|
+
{% else %}
|
124
|
+
# Example implementation for JSON request:
|
125
|
+
request = {
|
126
|
+
"pickupDate": pickup_date,
|
127
|
+
"readyTime": ready_time,
|
128
|
+
"closingTime": closing_time,
|
129
|
+
"address": {
|
130
|
+
"addressLine1": address.address_line1,
|
131
|
+
"city": address.city,
|
132
|
+
"postalCode": address.postal_code,
|
133
|
+
"countryCode": address.country_code,
|
134
|
+
"stateCode": address.state_code,
|
135
|
+
"personName": address.person_name,
|
136
|
+
"companyName": address.company_name,
|
137
|
+
"phoneNumber": address.phone_number,
|
138
|
+
"email": address.email,
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
return lib.Serializable(request, lib.to_dict)
|
143
|
+
{% endif %}
|
144
|
+
'''
|
145
|
+
)
|
146
|
+
|
147
|
+
PROVIDER_PICKUP_UPDATE_TEMPLATE = Template('''"""Karrio {{name}} pickup update API implementation."""
|
148
|
+
|
149
|
+
import typing
|
150
|
+
import karrio.lib as lib
|
151
|
+
import karrio.core.models as models
|
152
|
+
import karrio.providers.{{id}}.error as error
|
153
|
+
import karrio.providers.{{id}}.utils as provider_utils
|
154
|
+
|
155
|
+
|
156
|
+
def parse_pickup_update_response(
|
157
|
+
_response: lib.Deserializable[{% if is_xml_api %}lib.Element{% else %}dict{% endif %}],
|
158
|
+
settings: provider_utils.Settings,
|
159
|
+
) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
|
160
|
+
"""
|
161
|
+
Parse pickup update response from carrier API
|
162
|
+
|
163
|
+
_response: The carrier response to deserialize
|
164
|
+
settings: The carrier connection settings
|
165
|
+
|
166
|
+
Returns a tuple with (PickupDetails, List[Message])
|
167
|
+
"""
|
168
|
+
response = _response.deserialize()
|
169
|
+
messages = error.parse_error_response(response, settings)
|
170
|
+
|
171
|
+
# Extract updated pickup details
|
172
|
+
pickup = _extract_details(response, settings)
|
173
|
+
|
174
|
+
return pickup, messages
|
175
|
+
|
176
|
+
|
177
|
+
def _extract_details(
|
178
|
+
response: {% if is_xml_api %}lib.Element{% else %}dict{% endif %},
|
179
|
+
settings: provider_utils.Settings,
|
180
|
+
) -> models.PickupDetails:
|
181
|
+
"""
|
182
|
+
Extract pickup details from carrier response data
|
183
|
+
|
184
|
+
data: The carrier-specific pickup response data
|
185
|
+
settings: The carrier connection settings
|
186
|
+
|
187
|
+
Returns a PickupDetails object with the pickup information
|
188
|
+
"""
|
189
|
+
{% if is_xml_api %}
|
190
|
+
# Example implementation for XML response:
|
191
|
+
# Extract pickup details from the XML response
|
192
|
+
# confirmation_number = lib.find_element("confirmation-number", response, first=True).text
|
193
|
+
# pickup_date = lib.find_element("pickup-date", response, first=True).text
|
194
|
+
# ready_time = lib.find_element("ready-time", response, first=True).text
|
195
|
+
# closing_time = lib.find_element("closing-time", response, first=True).text
|
196
|
+
|
197
|
+
# For development, return sample data
|
198
|
+
confirmation_number = "PICKUP123"
|
199
|
+
pickup_date = lib.today_str()
|
200
|
+
ready_time = "10:00"
|
201
|
+
closing_time = "18:00"
|
202
|
+
{% else %}
|
203
|
+
# Example implementation for JSON response:
|
204
|
+
# Extract pickup details from the JSON response
|
205
|
+
# confirmation_number = response.get("confirmationNumber")
|
206
|
+
# pickup_date = response.get("pickupDate")
|
207
|
+
# ready_time = response.get("readyTime")
|
208
|
+
# closing_time = response.get("closingTime")
|
209
|
+
|
210
|
+
# For development, return sample data
|
211
|
+
confirmation_number = "PICKUP123"
|
212
|
+
pickup_date = lib.today_str()
|
213
|
+
ready_time = "10:00"
|
214
|
+
closing_time = "18:00"
|
215
|
+
{% endif %}
|
216
|
+
|
217
|
+
return models.PickupDetails(
|
218
|
+
carrier_id=settings.carrier_id,
|
219
|
+
carrier_name=settings.carrier_name,
|
220
|
+
confirmation_number=confirmation_number,
|
221
|
+
pickup_date=lib.fdate(pickup_date),
|
222
|
+
ready_time=ready_time,
|
223
|
+
closing_time=closing_time,
|
224
|
+
)
|
225
|
+
|
226
|
+
|
227
|
+
def pickup_update_request(
|
228
|
+
payload: models.PickupUpdateRequest,
|
229
|
+
settings: provider_utils.Settings,
|
230
|
+
) -> lib.Serializable:
|
231
|
+
"""
|
232
|
+
Create a pickup update request for the carrier API
|
233
|
+
|
234
|
+
payload: The standardized PickupUpdateRequest from karrio
|
235
|
+
settings: The carrier connection settings
|
236
|
+
|
237
|
+
Returns a Serializable object that can be sent to the carrier API
|
238
|
+
"""
|
239
|
+
# Extract pickup update details
|
240
|
+
confirmation_number = payload.confirmation_number
|
241
|
+
address = lib.to_address(payload.address)
|
242
|
+
pickup_date = payload.pickup_date or lib.today_str()
|
243
|
+
ready_time = payload.ready_time or "10:00"
|
244
|
+
closing_time = payload.closing_time or "18:00"
|
245
|
+
|
246
|
+
{% if is_xml_api %}
|
247
|
+
# Example implementation for XML request:
|
248
|
+
request = f"""<?xml version="1.0"?>
|
249
|
+
<pickup-update-request>
|
250
|
+
<confirmation-number>{confirmation_number}</confirmation-number>
|
251
|
+
<pickup-date>{pickup_date}</pickup-date>
|
252
|
+
<ready-time>{ready_time}</ready-time>
|
253
|
+
<closing-time>{closing_time}</closing-time>
|
254
|
+
<address>
|
255
|
+
<address-line1>{address.address_line1}</address-line1>
|
256
|
+
<city>{address.city}</city>
|
257
|
+
<postal-code>{address.postal_code}</postal-code>
|
258
|
+
<country-code>{address.country_code}</country-code>
|
259
|
+
<state-code>{address.state_code}</state-code>
|
260
|
+
<person-name>{address.person_name}</person-name>
|
261
|
+
<company-name>{address.company_name}</company-name>
|
262
|
+
<phone-number>{address.phone_number}</phone-number>
|
263
|
+
<email>{address.email}</email>
|
264
|
+
</address>
|
265
|
+
</pickup-update-request>"""
|
266
|
+
|
267
|
+
return lib.Serializable(request, lambda r: r)
|
268
|
+
{% else %}
|
269
|
+
# Example implementation for JSON request:
|
270
|
+
request = {
|
271
|
+
"confirmationNumber": confirmation_number,
|
272
|
+
"pickupDate": pickup_date,
|
273
|
+
"readyTime": ready_time,
|
274
|
+
"closingTime": closing_time,
|
275
|
+
"address": {
|
276
|
+
"addressLine1": address.address_line1,
|
277
|
+
"city": address.city,
|
278
|
+
"postalCode": address.postal_code,
|
279
|
+
"countryCode": address.country_code,
|
280
|
+
"stateCode": address.state_code,
|
281
|
+
"personName": address.person_name,
|
282
|
+
"companyName": address.company_name,
|
283
|
+
"phoneNumber": address.phone_number,
|
284
|
+
"email": address.email,
|
285
|
+
}
|
286
|
+
}
|
287
|
+
|
288
|
+
return lib.Serializable(request, lib.to_dict)
|
289
|
+
{% endif %}
|
290
|
+
'''
|
291
|
+
)
|
292
|
+
|
293
|
+
PROVIDER_PICKUP_CANCEL_TEMPLATE = Template('''"""Karrio {{name}} pickup cancellation API implementation."""
|
294
|
+
|
295
|
+
import typing
|
296
|
+
import karrio.lib as lib
|
297
|
+
import karrio.core.models as models
|
298
|
+
import karrio.providers.{{id}}.error as error
|
299
|
+
import karrio.providers.{{id}}.utils as provider_utils
|
300
|
+
|
301
|
+
|
302
|
+
def parse_pickup_cancel_response(
|
303
|
+
_response: lib.Deserializable[{% if is_xml_api %}lib.Element{% else %}dict{% endif %}],
|
304
|
+
settings: provider_utils.Settings,
|
305
|
+
) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
|
306
|
+
"""Parse pickup cancellation response from carrier API"""
|
307
|
+
response = _response.deserialize()
|
308
|
+
messages = error.parse_error_response(response, settings)
|
309
|
+
|
310
|
+
# Check if cancellation was successful
|
311
|
+
success = _extract_cancellation_status(response)
|
312
|
+
confirmation = (
|
313
|
+
models.ConfirmationDetails(
|
314
|
+
carrier_id=settings.carrier_id,
|
315
|
+
carrier_name=settings.carrier_name,
|
316
|
+
success=success,
|
317
|
+
operation="Cancel Pickup",
|
318
|
+
) if success else None
|
319
|
+
)
|
320
|
+
|
321
|
+
return confirmation, messages
|
322
|
+
|
323
|
+
|
324
|
+
def _extract_cancellation_status(
|
325
|
+
response: {% if is_xml_api %}lib.Element{% else %}dict{% endif %}
|
326
|
+
) -> bool:
|
327
|
+
"""Extract cancellation success status from carrier response"""
|
328
|
+
{% if is_xml_api %}
|
329
|
+
# Example implementation for XML response:
|
330
|
+
# status_node = lib.find_element("status", response, first=True)
|
331
|
+
# return status_node is not None and status_node.text.lower() == "cancelled"
|
332
|
+
|
333
|
+
# For development, always return success
|
334
|
+
return True
|
335
|
+
{% else %}
|
336
|
+
# Example implementation for JSON response:
|
337
|
+
# return response.get("status", "").lower() == "cancelled"
|
338
|
+
|
339
|
+
# For development, always return success
|
340
|
+
return True
|
341
|
+
{% endif %}
|
342
|
+
|
343
|
+
|
344
|
+
def pickup_cancel_request(
|
345
|
+
payload: models.PickupCancelRequest,
|
346
|
+
settings: provider_utils.Settings,
|
347
|
+
) -> lib.Serializable:
|
348
|
+
"""Create pickup cancellation request for carrier API"""
|
349
|
+
# Extract cancellation details
|
350
|
+
confirmation_number = payload.confirmation_number
|
351
|
+
|
352
|
+
{% if is_xml_api %}
|
353
|
+
# Example implementation for XML request:
|
354
|
+
request = f"""<?xml version="1.0"?>
|
355
|
+
<pickup-cancel-request>
|
356
|
+
<confirmation-number>{confirmation_number}</confirmation-number>
|
357
|
+
</pickup-cancel-request>"""
|
358
|
+
|
359
|
+
return lib.Serializable(request, lambda r: r)
|
360
|
+
{% else %}
|
361
|
+
# Example implementation for JSON request:
|
362
|
+
request = {
|
363
|
+
"confirmationNumber": confirmation_number
|
364
|
+
}
|
365
|
+
|
366
|
+
return lib.Serializable(request, lib.to_dict)
|
367
|
+
{% endif %}
|
368
|
+
'''
|
369
|
+
)
|
370
|
+
|
371
|
+
PROVIDER_PICKUP_IMPORTS_TEMPLATE = Template(
|
372
|
+
'''"""Karrio {{name}} pickup API imports."""
|
373
|
+
|
374
|
+
from karrio.providers.{{id}}.pickup.create import (
|
375
|
+
parse_pickup_response,
|
376
|
+
pickup_request,
|
377
|
+
)
|
378
|
+
from karrio.providers.{{id}}.pickup.update import (
|
379
|
+
parse_pickup_update_response,
|
380
|
+
pickup_update_request,
|
381
|
+
)
|
382
|
+
from karrio.providers.{{id}}.pickup.cancel import (
|
383
|
+
parse_pickup_cancel_response,
|
384
|
+
pickup_cancel_request,
|
385
|
+
)
|
386
|
+
'''
|
387
|
+
)
|
388
|
+
|
389
|
+
|
390
|
+
# XML schema templates for pickup operations
|
391
|
+
XML_SCHEMA_PICKUP_CREATE_REQUEST_TEMPLATE = Template("""<?xml version="1.0"?>
|
392
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/pickup-request" xmlns="http://{{id}}.com/ws/pickup-request" elementFormDefault="qualified">
|
393
|
+
<xsd:element name="pickup-request">
|
394
|
+
<xsd:complexType>
|
395
|
+
<xsd:all>
|
396
|
+
<xsd:element name="account-number" type="xsd:string" minOccurs="0" />
|
397
|
+
<xsd:element name="pickup-date" type="xsd:date" />
|
398
|
+
<xsd:element name="ready-time" type="xsd:string" />
|
399
|
+
<xsd:element name="closing-time" type="xsd:string" />
|
400
|
+
<xsd:element name="instruction" type="xsd:string" minOccurs="0" />
|
401
|
+
<xsd:element name="address">
|
402
|
+
<xsd:complexType>
|
403
|
+
<xsd:all>
|
404
|
+
<xsd:element name="company-name" type="xsd:string" minOccurs="0" />
|
405
|
+
<xsd:element name="person-name" type="xsd:string" />
|
406
|
+
<xsd:element name="street" type="xsd:string" />
|
407
|
+
<xsd:element name="city" type="xsd:string" />
|
408
|
+
<xsd:element name="state" type="xsd:string" minOccurs="0" />
|
409
|
+
<xsd:element name="postal-code" type="xsd:string" />
|
410
|
+
<xsd:element name="country" type="xsd:string" />
|
411
|
+
<xsd:element name="phone" type="xsd:string" minOccurs="0" />
|
412
|
+
<xsd:element name="email" type="xsd:string" minOccurs="0" />
|
413
|
+
</xsd:all>
|
414
|
+
</xsd:complexType>
|
415
|
+
</xsd:element>
|
416
|
+
<xsd:element name="parcel-count" type="xsd:integer" minOccurs="0" />
|
417
|
+
<xsd:element name="weight" type="xsd:decimal" minOccurs="0" />
|
418
|
+
<xsd:element name="weight-unit" type="xsd:string" minOccurs="0" />
|
419
|
+
</xsd:all>
|
420
|
+
</xsd:complexType>
|
421
|
+
</xsd:element>
|
422
|
+
</xsd:schema>
|
423
|
+
"""
|
424
|
+
)
|
425
|
+
|
426
|
+
XML_SCHEMA_PICKUP_CREATE_RESPONSE_TEMPLATE = Template("""<?xml version="1.0"?>
|
427
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/pickup-response" xmlns="http://{{id}}.com/ws/pickup-response" elementFormDefault="qualified">
|
428
|
+
<xsd:element name="pickup-response">
|
429
|
+
<xsd:complexType>
|
430
|
+
<xsd:all>
|
431
|
+
<xsd:element name="confirmation-number" type="xsd:string" />
|
432
|
+
<xsd:element name="pickup-date" type="xsd:date" />
|
433
|
+
<xsd:element name="ready-time" type="xsd:string" minOccurs="0" />
|
434
|
+
<xsd:element name="closing-time" type="xsd:string" minOccurs="0" />
|
435
|
+
<xsd:element name="status" type="xsd:string" minOccurs="0" />
|
436
|
+
<xsd:element name="request-id" type="xsd:string" minOccurs="0" />
|
437
|
+
</xsd:all>
|
438
|
+
</xsd:complexType>
|
439
|
+
</xsd:element>
|
440
|
+
</xsd:schema>
|
441
|
+
"""
|
442
|
+
)
|
443
|
+
|
444
|
+
XML_SCHEMA_PICKUP_UPDATE_REQUEST_TEMPLATE = Template("""<?xml version="1.0"?>
|
445
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/pickup-update-request" xmlns="http://{{id}}.com/ws/pickup-update-request" elementFormDefault="qualified">
|
446
|
+
<xsd:element name="pickup-update-request">
|
447
|
+
<xsd:complexType>
|
448
|
+
<xsd:all>
|
449
|
+
<xsd:element name="confirmation-number" type="xsd:string" />
|
450
|
+
<xsd:element name="account-number" type="xsd:string" minOccurs="0" />
|
451
|
+
<xsd:element name="pickup-date" type="xsd:date" />
|
452
|
+
<xsd:element name="ready-time" type="xsd:string" />
|
453
|
+
<xsd:element name="closing-time" type="xsd:string" />
|
454
|
+
<xsd:element name="instruction" type="xsd:string" minOccurs="0" />
|
455
|
+
<xsd:element name="address">
|
456
|
+
<xsd:complexType>
|
457
|
+
<xsd:all>
|
458
|
+
<xsd:element name="company-name" type="xsd:string" minOccurs="0" />
|
459
|
+
<xsd:element name="person-name" type="xsd:string" />
|
460
|
+
<xsd:element name="street" type="xsd:string" />
|
461
|
+
<xsd:element name="city" type="xsd:string" />
|
462
|
+
<xsd:element name="state" type="xsd:string" minOccurs="0" />
|
463
|
+
<xsd:element name="postal-code" type="xsd:string" />
|
464
|
+
<xsd:element name="country" type="xsd:string" />
|
465
|
+
<xsd:element name="phone" type="xsd:string" minOccurs="0" />
|
466
|
+
<xsd:element name="email" type="xsd:string" minOccurs="0" />
|
467
|
+
</xsd:all>
|
468
|
+
</xsd:complexType>
|
469
|
+
</xsd:element>
|
470
|
+
<xsd:element name="parcel-count" type="xsd:integer" minOccurs="0" />
|
471
|
+
<xsd:element name="weight" type="xsd:decimal" minOccurs="0" />
|
472
|
+
<xsd:element name="weight-unit" type="xsd:string" minOccurs="0" />
|
473
|
+
</xsd:all>
|
474
|
+
</xsd:complexType>
|
475
|
+
</xsd:element>
|
476
|
+
</xsd:schema>
|
477
|
+
"""
|
478
|
+
)
|
479
|
+
|
480
|
+
XML_SCHEMA_PICKUP_UPDATE_RESPONSE_TEMPLATE = Template("""<?xml version="1.0"?>
|
481
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/pickup-update-response" xmlns="http://{{id}}.com/ws/pickup-update-response" elementFormDefault="qualified">
|
482
|
+
<xsd:element name="pickup-update-response">
|
483
|
+
<xsd:complexType>
|
484
|
+
<xsd:all>
|
485
|
+
<xsd:element name="confirmation-number" type="xsd:string" />
|
486
|
+
<xsd:element name="pickup-date" type="xsd:date" />
|
487
|
+
<xsd:element name="ready-time" type="xsd:string" minOccurs="0" />
|
488
|
+
<xsd:element name="closing-time" type="xsd:string" minOccurs="0" />
|
489
|
+
<xsd:element name="status" type="xsd:string" minOccurs="0" />
|
490
|
+
<xsd:element name="request-id" type="xsd:string" minOccurs="0" />
|
491
|
+
</xsd:all>
|
492
|
+
</xsd:complexType>
|
493
|
+
</xsd:element>
|
494
|
+
</xsd:schema>
|
495
|
+
"""
|
496
|
+
)
|
497
|
+
|
498
|
+
XML_SCHEMA_PICKUP_CANCEL_REQUEST_TEMPLATE = Template("""<?xml version="1.0"?>
|
499
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/pickup-cancel-request" xmlns="http://{{id}}.com/ws/pickup-cancel-request" elementFormDefault="qualified">
|
500
|
+
<xsd:element name="pickup-cancel-request">
|
501
|
+
<xsd:complexType>
|
502
|
+
<xsd:all>
|
503
|
+
<xsd:element name="confirmation-number" type="xsd:string" />
|
504
|
+
<xsd:element name="pickup-date" type="xsd:date" minOccurs="0" />
|
505
|
+
<xsd:element name="reason" type="xsd:string" minOccurs="0" />
|
506
|
+
<xsd:element name="account-number" type="xsd:string" minOccurs="0" />
|
507
|
+
</xsd:all>
|
508
|
+
</xsd:complexType>
|
509
|
+
</xsd:element>
|
510
|
+
</xsd:schema>
|
511
|
+
"""
|
512
|
+
)
|
513
|
+
|
514
|
+
XML_SCHEMA_PICKUP_CANCEL_RESPONSE_TEMPLATE = Template("""<?xml version="1.0"?>
|
515
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/pickup-cancel-response" xmlns="http://{{id}}.com/ws/pickup-cancel-response" elementFormDefault="qualified">
|
516
|
+
<xsd:element name="pickup-cancel-response">
|
517
|
+
<xsd:complexType>
|
518
|
+
<xsd:all>
|
519
|
+
<xsd:element name="status" type="xsd:string" />
|
520
|
+
<xsd:element name="confirmation-number" type="xsd:string" minOccurs="0" />
|
521
|
+
<xsd:element name="status-message" type="xsd:string" minOccurs="0" />
|
522
|
+
</xsd:all>
|
523
|
+
</xsd:complexType>
|
524
|
+
</xsd:element>
|
525
|
+
</xsd:schema>
|
526
|
+
"""
|
527
|
+
)
|
528
|
+
|
529
|
+
# JSON schema templates for pickup operations
|
530
|
+
JSON_SCHEMA_PICKUP_CREATE_REQUEST_TEMPLATE = Template(
|
531
|
+
"""{
|
532
|
+
"pickupRequest": {
|
533
|
+
"accountNumber": "123456",
|
534
|
+
"pickupDate": "2023-06-01",
|
535
|
+
"readyTime": "09:00",
|
536
|
+
"closingTime": "17:00",
|
537
|
+
"instruction": "Please knock loudly",
|
538
|
+
"address": {
|
539
|
+
"companyName": "ACME Corp",
|
540
|
+
"personName": "John Doe",
|
541
|
+
"street": "123 Main St",
|
542
|
+
"city": "Anytown",
|
543
|
+
"state": "CA",
|
544
|
+
"postalCode": "12345",
|
545
|
+
"country": "US",
|
546
|
+
"phone": "555-123-4567",
|
547
|
+
"email": "john@example.com"
|
548
|
+
},
|
549
|
+
"parcelCount": 3,
|
550
|
+
"weight": 10.5,
|
551
|
+
"weightUnit": "KG"
|
552
|
+
}
|
553
|
+
}
|
554
|
+
"""
|
555
|
+
)
|
556
|
+
|
557
|
+
JSON_SCHEMA_PICKUP_CREATE_RESPONSE_TEMPLATE = Template(
|
558
|
+
"""{
|
559
|
+
"pickupResponse": {
|
560
|
+
"confirmationNumber": "PICKUP123456",
|
561
|
+
"pickupDate": "2023-06-01",
|
562
|
+
"readyTime": "09:00",
|
563
|
+
"closingTime": "17:00",
|
564
|
+
"status": "scheduled",
|
565
|
+
"requestId": "REQ123456"
|
566
|
+
}
|
567
|
+
}
|
568
|
+
"""
|
569
|
+
)
|
570
|
+
|
571
|
+
JSON_SCHEMA_PICKUP_UPDATE_REQUEST_TEMPLATE = Template(
|
572
|
+
"""{
|
573
|
+
"pickupUpdateRequest": {
|
574
|
+
"confirmationNumber": "PICKUP123456",
|
575
|
+
"accountNumber": "123456",
|
576
|
+
"pickupDate": "2023-06-01",
|
577
|
+
"readyTime": "10:00",
|
578
|
+
"closingTime": "18:00",
|
579
|
+
"instruction": "Please knock loudly and call before arrival",
|
580
|
+
"address": {
|
581
|
+
"companyName": "ACME Corp",
|
582
|
+
"personName": "John Doe",
|
583
|
+
"street": "123 Main St",
|
584
|
+
"city": "Anytown",
|
585
|
+
"state": "CA",
|
586
|
+
"postalCode": "12345",
|
587
|
+
"country": "US",
|
588
|
+
"phone": "555-123-4567",
|
589
|
+
"email": "john@example.com"
|
590
|
+
},
|
591
|
+
"parcelCount": 5,
|
592
|
+
"weight": 15.5,
|
593
|
+
"weightUnit": "KG"
|
594
|
+
}
|
595
|
+
}
|
596
|
+
"""
|
597
|
+
)
|
598
|
+
|
599
|
+
JSON_SCHEMA_PICKUP_UPDATE_RESPONSE_TEMPLATE = Template(
|
600
|
+
"""{
|
601
|
+
"pickupUpdateResponse": {
|
602
|
+
"confirmationNumber": "PICKUP123456",
|
603
|
+
"pickupDate": "2023-06-01",
|
604
|
+
"readyTime": "10:00",
|
605
|
+
"closingTime": "18:00",
|
606
|
+
"status": "modified",
|
607
|
+
"requestId": "REQ123456"
|
608
|
+
}
|
609
|
+
}
|
610
|
+
"""
|
611
|
+
)
|
612
|
+
|
613
|
+
JSON_SCHEMA_PICKUP_CANCEL_REQUEST_TEMPLATE = Template(
|
614
|
+
"""{
|
615
|
+
"pickupCancelRequest": {
|
616
|
+
"confirmationNumber": "PICKUP123456",
|
617
|
+
"accountNumber": "123456",
|
618
|
+
"pickupDate": "2023-06-01",
|
619
|
+
"reason": "No longer needed"
|
620
|
+
}
|
621
|
+
}
|
622
|
+
"""
|
623
|
+
)
|
624
|
+
|
625
|
+
JSON_SCHEMA_PICKUP_CANCEL_RESPONSE_TEMPLATE = Template(
|
626
|
+
"""{
|
627
|
+
"pickupCancelResponse": {
|
628
|
+
"status": "cancelled",
|
629
|
+
"confirmationNumber": "PICKUP123456",
|
630
|
+
"statusMessage": "Pickup successfully cancelled"
|
631
|
+
}
|
632
|
+
}
|
633
|
+
"""
|
634
|
+
)
|
635
|
+
|
636
|
+
|
637
|
+
TEST_PICKUP_TEMPLATE = Template('''"""{{name}} carrier pickup tests."""
|
638
|
+
|
639
|
+
import unittest
|
640
|
+
import karrio.sdk as karrio
|
641
|
+
import karrio.lib as lib
|
642
|
+
import karrio.core.models as models
|
643
|
+
from unittest.mock import patch
|
644
|
+
from .fixture import gateway
|
645
|
+
|
646
|
+
|
647
|
+
class TestPickup(unittest.TestCase):
|
648
|
+
def setUp(self):
|
649
|
+
self.maxDiff = None
|
650
|
+
self.PickupRequest = models.PickupRequest(**PickupPayload)
|
651
|
+
|
652
|
+
def test_create_pickup_request(self):
|
653
|
+
request = gateway.mapper.create_pickup_request(self.PickupRequest)
|
654
|
+
self.assertEqual(lib.to_dict(request.serialize()), PickupRequest)
|
655
|
+
|
656
|
+
def test_schedule_pickup(self):
|
657
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
658
|
+
mock.return_value = {% if is_xml_api %}"<r></r>"{% else %}"{}"{% endif %}
|
659
|
+
karrio.Pickup.schedule(self.PickupRequest).from_(gateway)
|
660
|
+
self.assertEqual(
|
661
|
+
mock.call_args[1]["url"],
|
662
|
+
f"{gateway.settings.server_url}/pickups"
|
663
|
+
)
|
664
|
+
|
665
|
+
def test_update_pickup(self):
|
666
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
667
|
+
mock.return_value = {% if is_xml_api %}"<r></r>"{% else %}"{}"{% endif %}
|
668
|
+
karrio.Pickup.update(self.PickupRequest).from_(gateway)
|
669
|
+
self.assertEqual(
|
670
|
+
mock.call_args[1]["url"],
|
671
|
+
f"{gateway.settings.server_url}/pickups/123/update"
|
672
|
+
)
|
673
|
+
|
674
|
+
def test_cancel_pickup(self):
|
675
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
676
|
+
mock.return_value = {% if is_xml_api %}"<r></r>"{% else %}"{}"{% endif %}
|
677
|
+
karrio.Pickup.cancel(self.PickupRequest).from_(gateway)
|
678
|
+
self.assertEqual(
|
679
|
+
mock.call_args[1]["url"],
|
680
|
+
f"{gateway.settings.server_url}/pickups/123/cancel"
|
681
|
+
)
|
682
|
+
|
683
|
+
def test_parse_pickup_response(self):
|
684
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
685
|
+
mock.return_value = PickupResponse
|
686
|
+
parsed_response = (
|
687
|
+
karrio.Pickup.schedule(self.PickupRequest)
|
688
|
+
.from_(gateway)
|
689
|
+
.parse()
|
690
|
+
)
|
691
|
+
self.assertListEqual(lib.to_dict(parsed_response), ParsedPickupResponse)
|
692
|
+
|
693
|
+
def test_parse_error_response(self):
|
694
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
695
|
+
mock.return_value = ErrorResponse
|
696
|
+
parsed_response = (
|
697
|
+
karrio.Pickup.schedule(self.PickupRequest)
|
698
|
+
.from_(gateway)
|
699
|
+
.parse()
|
700
|
+
)
|
701
|
+
self.assertListEqual(lib.to_dict(parsed_response), ParsedErrorResponse)
|
702
|
+
|
703
|
+
|
704
|
+
if __name__ == "__main__":
|
705
|
+
unittest.main()
|
706
|
+
|
707
|
+
|
708
|
+
PickupPayload = {
|
709
|
+
"address": {
|
710
|
+
"address_line1": "123 Test Street",
|
711
|
+
"city": "Test City",
|
712
|
+
"postal_code": "12345",
|
713
|
+
"country_code": "US",
|
714
|
+
"state_code": "CA",
|
715
|
+
"person_name": "Test Person",
|
716
|
+
"company_name": "Test Company",
|
717
|
+
"phone_number": "1234567890",
|
718
|
+
"email": "test@example.com"
|
719
|
+
},
|
720
|
+
"pickup_date": "2024-01-01",
|
721
|
+
"ready_time": "09:00",
|
722
|
+
"closing_time": "17:00",
|
723
|
+
"confirmation_number": "123"
|
724
|
+
}
|
725
|
+
|
726
|
+
PickupRequest = {% if is_xml_api %}{
|
727
|
+
"address": {
|
728
|
+
"address_line1": "123 Test Street",
|
729
|
+
"city": "Test City",
|
730
|
+
"postal_code": "12345",
|
731
|
+
"country_code": "US",
|
732
|
+
"state_code": "CA",
|
733
|
+
"person_name": "Test Person",
|
734
|
+
"company_name": "Test Company",
|
735
|
+
"phone_number": "1234567890",
|
736
|
+
"email": "test@example.com"
|
737
|
+
},
|
738
|
+
"pickup_date": "2024-01-01",
|
739
|
+
"ready_time": "09:00",
|
740
|
+
"closing_time": "17:00"
|
741
|
+
}{% else %}{
|
742
|
+
"address": {
|
743
|
+
"addressLine1": "123 Test Street",
|
744
|
+
"city": "Test City",
|
745
|
+
"postalCode": "12345",
|
746
|
+
"countryCode": "US",
|
747
|
+
"stateCode": "CA",
|
748
|
+
"personName": "Test Person",
|
749
|
+
"companyName": "Test Company",
|
750
|
+
"phoneNumber": "1234567890",
|
751
|
+
"email": "test@example.com"
|
752
|
+
},
|
753
|
+
"pickupDate": "2024-01-01",
|
754
|
+
"readyTime": "09:00",
|
755
|
+
"closingTime": "17:00"
|
756
|
+
}{% endif %}
|
757
|
+
|
758
|
+
PickupResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
759
|
+
<pickup-response>
|
760
|
+
<confirmation-number>PICKUP123</confirmation-number>
|
761
|
+
<pickup-date>2024-01-01</pickup-date>
|
762
|
+
<ready-time>09:00</ready-time>
|
763
|
+
<closing-time>17:00</closing-time>
|
764
|
+
<status>scheduled</status>
|
765
|
+
</pickup-response>"""{% else %}"""{
|
766
|
+
"confirmationNumber": "PICKUP123",
|
767
|
+
"pickupDate": "2024-01-01",
|
768
|
+
"readyTime": "09:00",
|
769
|
+
"closingTime": "17:00",
|
770
|
+
"status": "scheduled"
|
771
|
+
}"""{% endif %}
|
772
|
+
|
773
|
+
PickupUpdateResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
774
|
+
<pickup-update-response>
|
775
|
+
<confirmation-number>PICKUP123</confirmation-number>
|
776
|
+
<pickup-date>2024-01-02</pickup-date>
|
777
|
+
<ready-time>10:00</ready-time>
|
778
|
+
<closing-time>18:00</closing-time>
|
779
|
+
<status>updated</status>
|
780
|
+
</pickup-update-response>"""{% else %}"""{
|
781
|
+
"confirmationNumber": "PICKUP123",
|
782
|
+
"pickupDate": "2024-01-02",
|
783
|
+
"readyTime": "10:00",
|
784
|
+
"closingTime": "18:00",
|
785
|
+
"status": "updated"
|
786
|
+
}"""{% endif %}
|
787
|
+
|
788
|
+
PickupCancelResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
789
|
+
<pickup-cancel-response>
|
790
|
+
<success>true</success>
|
791
|
+
<message>Pickup successfully cancelled</message>
|
792
|
+
</pickup-cancel-response>"""{% else %}"""{
|
793
|
+
"success": true,
|
794
|
+
"message": "Pickup successfully cancelled"
|
795
|
+
}"""{% endif %}
|
796
|
+
|
797
|
+
ErrorResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
798
|
+
<error-response>
|
799
|
+
<e>
|
800
|
+
<code>pickup_error</code>
|
801
|
+
<message>Unable to schedule pickup</message>
|
802
|
+
<details>Invalid pickup date provided</details>
|
803
|
+
</e>
|
804
|
+
</error-response>"""{% else %}"""{
|
805
|
+
"error": {
|
806
|
+
"code": "pickup_error",
|
807
|
+
"message": "Unable to schedule pickup",
|
808
|
+
"details": "Invalid pickup date provided"
|
809
|
+
}
|
810
|
+
}"""{% endif %}
|
811
|
+
|
812
|
+
ParsedPickupResponse = [
|
813
|
+
{
|
814
|
+
"carrier_id": "{{id}}",
|
815
|
+
"carrier_name": "{{id}}",
|
816
|
+
"confirmation_number": "PICKUP123",
|
817
|
+
"pickup_date": "2024-01-01",
|
818
|
+
"ready_time": "09:00",
|
819
|
+
"closing_time": "17:00",
|
820
|
+
},
|
821
|
+
[]
|
822
|
+
]
|
823
|
+
|
824
|
+
ParsedErrorResponse = [
|
825
|
+
None,
|
826
|
+
[
|
827
|
+
{
|
828
|
+
"carrier_id": "{{id}}",
|
829
|
+
"carrier_name": "{{id}}",
|
830
|
+
"code": "pickup_error",
|
831
|
+
"message": "Unable to schedule pickup",
|
832
|
+
"details": {
|
833
|
+
"details": "Invalid pickup date provided"
|
834
|
+
}
|
835
|
+
}
|
836
|
+
]
|
837
|
+
]
|
838
|
+
'''
|
839
|
+
)
|