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,101 @@
|
|
1
|
+
GET_LOGS = """
|
2
|
+
query get_logs($filter: LogFilter) {
|
3
|
+
logs(filter: $filter) {
|
4
|
+
page_info {
|
5
|
+
count
|
6
|
+
has_next_page
|
7
|
+
has_previous_page
|
8
|
+
start_cursor
|
9
|
+
end_cursor
|
10
|
+
}
|
11
|
+
edges {
|
12
|
+
node {
|
13
|
+
id
|
14
|
+
path
|
15
|
+
host
|
16
|
+
data
|
17
|
+
method
|
18
|
+
response_ms
|
19
|
+
remote_addr
|
20
|
+
requested_at
|
21
|
+
status_code
|
22
|
+
query_params
|
23
|
+
response
|
24
|
+
records {
|
25
|
+
id
|
26
|
+
key
|
27
|
+
timestamp
|
28
|
+
test_mode
|
29
|
+
created_at
|
30
|
+
meta
|
31
|
+
record
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
"""
|
38
|
+
|
39
|
+
GET_LOG = """
|
40
|
+
query get_log($id: Int!) {
|
41
|
+
log(id: $id) {
|
42
|
+
id
|
43
|
+
requested_at
|
44
|
+
response_ms
|
45
|
+
path
|
46
|
+
remote_addr
|
47
|
+
host
|
48
|
+
method
|
49
|
+
query_params
|
50
|
+
data
|
51
|
+
response
|
52
|
+
status_code
|
53
|
+
records {
|
54
|
+
id
|
55
|
+
key
|
56
|
+
timestamp
|
57
|
+
test_mode
|
58
|
+
created_at
|
59
|
+
meta
|
60
|
+
record
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
"""
|
65
|
+
|
66
|
+
GET_EVENTS = """
|
67
|
+
query get_events($filter: EventFilter) {
|
68
|
+
events(filter: $filter) {
|
69
|
+
page_info {
|
70
|
+
count
|
71
|
+
has_next_page
|
72
|
+
has_previous_page
|
73
|
+
start_cursor
|
74
|
+
end_cursor
|
75
|
+
}
|
76
|
+
edges {
|
77
|
+
node {
|
78
|
+
id
|
79
|
+
type
|
80
|
+
data
|
81
|
+
test_mode
|
82
|
+
pending_webhooks
|
83
|
+
created_at
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
"""
|
89
|
+
|
90
|
+
GET_EVENT = """
|
91
|
+
query get_event($id: String!) {
|
92
|
+
event(id: $id) {
|
93
|
+
id
|
94
|
+
type
|
95
|
+
data
|
96
|
+
test_mode
|
97
|
+
pending_webhooks
|
98
|
+
created_at
|
99
|
+
}
|
100
|
+
}
|
101
|
+
"""
|
@@ -0,0 +1,368 @@
|
|
1
|
+
import json
|
2
|
+
import enum
|
3
|
+
import pydoc
|
4
|
+
import typer
|
5
|
+
import requests
|
6
|
+
import importlib
|
7
|
+
import karrio_cli.commands.login as login
|
8
|
+
from rich.syntax import Syntax
|
9
|
+
from rich.console import Console
|
10
|
+
from .queries import GET_LOGS, GET_LOG, GET_EVENTS, GET_EVENT
|
11
|
+
|
12
|
+
DEFAULT_FEATURES = [
|
13
|
+
"tracking",
|
14
|
+
"rating",
|
15
|
+
"shipping",
|
16
|
+
]
|
17
|
+
|
18
|
+
console = Console()
|
19
|
+
|
20
|
+
# GraphQL Queries
|
21
|
+
GET_EVENTS = """
|
22
|
+
query get_events($filter: EventFilter) {
|
23
|
+
events(filter: $filter) {
|
24
|
+
page_info {
|
25
|
+
count
|
26
|
+
has_next_page
|
27
|
+
has_previous_page
|
28
|
+
start_cursor
|
29
|
+
end_cursor
|
30
|
+
}
|
31
|
+
edges {
|
32
|
+
node {
|
33
|
+
id
|
34
|
+
type
|
35
|
+
data
|
36
|
+
test_mode
|
37
|
+
pending_webhooks
|
38
|
+
created_at
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
"""
|
44
|
+
|
45
|
+
GET_EVENT = """
|
46
|
+
query get_event($id: String!) {
|
47
|
+
event(id: $id) {
|
48
|
+
id
|
49
|
+
type
|
50
|
+
data
|
51
|
+
test_mode
|
52
|
+
pending_webhooks
|
53
|
+
created_at
|
54
|
+
}
|
55
|
+
}
|
56
|
+
"""
|
57
|
+
|
58
|
+
def parse_json_or_xml_string(value: str):
|
59
|
+
"""Parse a string that might be JSON or XML."""
|
60
|
+
if not value or not isinstance(value, str):
|
61
|
+
return value
|
62
|
+
|
63
|
+
# Try to parse as JSON first
|
64
|
+
try:
|
65
|
+
return json.loads(value)
|
66
|
+
except json.JSONDecodeError:
|
67
|
+
# If not JSON, return as is (could be XML or other format)
|
68
|
+
return value
|
69
|
+
|
70
|
+
|
71
|
+
def parse_records(records):
|
72
|
+
"""Parse record data and response fields."""
|
73
|
+
if not records:
|
74
|
+
return records
|
75
|
+
|
76
|
+
return [
|
77
|
+
{
|
78
|
+
**record,
|
79
|
+
"record": {
|
80
|
+
**record["record"],
|
81
|
+
"data": parse_json_or_xml_string(record["record"].get("data")),
|
82
|
+
"response": parse_json_or_xml_string(record["record"].get("response")),
|
83
|
+
} if record.get("record") else record["record"]
|
84
|
+
}
|
85
|
+
for record in records
|
86
|
+
]
|
87
|
+
|
88
|
+
|
89
|
+
def parse_log_response(data):
|
90
|
+
"""Parse log response data, including nested records."""
|
91
|
+
if not data:
|
92
|
+
return data
|
93
|
+
|
94
|
+
# Handle logs list response
|
95
|
+
if "logs" in data:
|
96
|
+
edges = data["logs"].get("edges", [])
|
97
|
+
for edge in edges:
|
98
|
+
node = edge.get("node", {})
|
99
|
+
if node:
|
100
|
+
node["response"] = parse_json_or_xml_string(node.get("response"))
|
101
|
+
node["data"] = parse_json_or_xml_string(node.get("data"))
|
102
|
+
node["records"] = parse_records(node.get("records", []))
|
103
|
+
return data
|
104
|
+
|
105
|
+
# Handle single log response
|
106
|
+
if "log" in data:
|
107
|
+
log = data["log"]
|
108
|
+
if log:
|
109
|
+
log["response"] = parse_json_or_xml_string(log.get("response"))
|
110
|
+
log["data"] = parse_json_or_xml_string(log.get("data"))
|
111
|
+
log["records"] = parse_records(log.get("records", []))
|
112
|
+
return data
|
113
|
+
|
114
|
+
return data
|
115
|
+
|
116
|
+
|
117
|
+
def format_json_output(data, pretty_print=False, line_numbers=False):
|
118
|
+
json_str = json.dumps(data, default=str, indent=2)
|
119
|
+
if pretty_print:
|
120
|
+
syntax = Syntax(json_str, "json", theme="ansi_light", line_numbers=line_numbers)
|
121
|
+
console.print(syntax)
|
122
|
+
else:
|
123
|
+
typer.echo(json_str)
|
124
|
+
|
125
|
+
|
126
|
+
def get_api_url_and_headers():
|
127
|
+
host, api_key = login.get_host_and_key()
|
128
|
+
headers = {"Authorization": f"Token {api_key}"} if api_key else {}
|
129
|
+
return host, headers
|
130
|
+
|
131
|
+
|
132
|
+
def make_request(
|
133
|
+
method,
|
134
|
+
endpoint: str,
|
135
|
+
payload: dict = None,
|
136
|
+
params: dict = None,
|
137
|
+
pretty_print: bool = False,
|
138
|
+
line_numbers: bool = False,
|
139
|
+
):
|
140
|
+
api_url, headers = get_api_url_and_headers()
|
141
|
+
url = f"{api_url}/{endpoint}"
|
142
|
+
|
143
|
+
try:
|
144
|
+
if method == "GET":
|
145
|
+
response = requests.get(url, headers=headers, params=params)
|
146
|
+
elif method == "POST":
|
147
|
+
response = requests.post(url, json=payload, headers=headers)
|
148
|
+
elif method == "PATCH":
|
149
|
+
response = requests.patch(url, json=payload, headers=headers)
|
150
|
+
elif method == "DELETE":
|
151
|
+
response = requests.delete(url, headers=headers)
|
152
|
+
else:
|
153
|
+
raise ValueError(f"Unsupported HTTP method: {method}")
|
154
|
+
|
155
|
+
response.raise_for_status()
|
156
|
+
|
157
|
+
if response.status_code == 204: # No Content
|
158
|
+
return None
|
159
|
+
|
160
|
+
data = response.json()
|
161
|
+
format_json_output(data, pretty_print, line_numbers)
|
162
|
+
return data
|
163
|
+
except requests.RequestException as e:
|
164
|
+
error_message = {"error": str(e)}
|
165
|
+
format_json_output(error_message, pretty_print, line_numbers)
|
166
|
+
return None
|
167
|
+
|
168
|
+
|
169
|
+
def make_get_request(
|
170
|
+
endpoint: str,
|
171
|
+
params: dict = None,
|
172
|
+
pretty_print: bool = False,
|
173
|
+
line_numbers: bool = False,
|
174
|
+
):
|
175
|
+
return make_request(
|
176
|
+
"GET",
|
177
|
+
endpoint,
|
178
|
+
params=params,
|
179
|
+
pretty_print=pretty_print,
|
180
|
+
line_numbers=line_numbers,
|
181
|
+
)
|
182
|
+
|
183
|
+
|
184
|
+
def make_post_request(
|
185
|
+
endpoint: str, payload: dict, pretty_print: bool = False, line_numbers: bool = False
|
186
|
+
):
|
187
|
+
return make_request(
|
188
|
+
"POST",
|
189
|
+
endpoint,
|
190
|
+
payload=payload,
|
191
|
+
pretty_print=pretty_print,
|
192
|
+
line_numbers=line_numbers,
|
193
|
+
)
|
194
|
+
|
195
|
+
|
196
|
+
def make_patch_request(
|
197
|
+
endpoint: str, payload: dict, pretty_print: bool = False, line_numbers: bool = False
|
198
|
+
):
|
199
|
+
return make_request(
|
200
|
+
"PATCH",
|
201
|
+
endpoint,
|
202
|
+
payload=payload,
|
203
|
+
pretty_print=pretty_print,
|
204
|
+
line_numbers=line_numbers,
|
205
|
+
)
|
206
|
+
|
207
|
+
|
208
|
+
def make_delete_request(
|
209
|
+
endpoint: str, pretty_print: bool = False, line_numbers: bool = False
|
210
|
+
):
|
211
|
+
return make_request(
|
212
|
+
"DELETE", endpoint, pretty_print=pretty_print, line_numbers=line_numbers
|
213
|
+
)
|
214
|
+
|
215
|
+
|
216
|
+
def parse_event_response(data):
|
217
|
+
"""Parse the GraphQL response for events."""
|
218
|
+
if "events" in data:
|
219
|
+
return {"events": data["events"]}
|
220
|
+
elif "event" in data:
|
221
|
+
return {"event": data["event"]}
|
222
|
+
return data
|
223
|
+
|
224
|
+
|
225
|
+
def make_graphql_request(
|
226
|
+
query_name: str,
|
227
|
+
variables: dict = None,
|
228
|
+
pretty_print: bool = False,
|
229
|
+
line_numbers: bool = False,
|
230
|
+
):
|
231
|
+
"""
|
232
|
+
Make a GraphQL request to the API.
|
233
|
+
|
234
|
+
Args:
|
235
|
+
query_name: Name of the query to execute (e.g., 'get_logs', 'get_log', 'get_events', 'get_event')
|
236
|
+
variables: Variables to pass to the query
|
237
|
+
pretty_print: Whether to pretty print the output
|
238
|
+
line_numbers: Whether to show line numbers in pretty print
|
239
|
+
"""
|
240
|
+
api_url, headers = get_api_url_and_headers()
|
241
|
+
url = f"{api_url}/graphql"
|
242
|
+
|
243
|
+
# Get the query based on the query name
|
244
|
+
query = {
|
245
|
+
"get_logs": GET_LOGS,
|
246
|
+
"get_log": GET_LOG,
|
247
|
+
"get_events": GET_EVENTS,
|
248
|
+
"get_event": GET_EVENT,
|
249
|
+
}.get(query_name)
|
250
|
+
|
251
|
+
if not query:
|
252
|
+
raise ValueError(f"Unknown query: {query_name}")
|
253
|
+
|
254
|
+
payload = {
|
255
|
+
"query": query,
|
256
|
+
"variables": variables or {}
|
257
|
+
}
|
258
|
+
|
259
|
+
try:
|
260
|
+
response = requests.post(url, json=payload, headers=headers)
|
261
|
+
response.raise_for_status()
|
262
|
+
|
263
|
+
data = response.json()
|
264
|
+
|
265
|
+
# Check for GraphQL errors
|
266
|
+
if "errors" in data:
|
267
|
+
error_message = {"error": data["errors"]}
|
268
|
+
format_json_output(error_message, pretty_print, line_numbers)
|
269
|
+
return None
|
270
|
+
|
271
|
+
# Extract and parse the data from the response
|
272
|
+
if query_name.startswith("get_event"):
|
273
|
+
result = parse_event_response(data.get("data", {}))
|
274
|
+
else:
|
275
|
+
result = parse_log_response(data.get("data", {}))
|
276
|
+
|
277
|
+
format_json_output(result, pretty_print, line_numbers)
|
278
|
+
return result
|
279
|
+
|
280
|
+
except requests.RequestException as e:
|
281
|
+
error_message = {"error": str(e)}
|
282
|
+
format_json_output(error_message, pretty_print, line_numbers)
|
283
|
+
return None
|
284
|
+
|
285
|
+
|
286
|
+
def gen(entity):
|
287
|
+
return pydoc.render_doc(entity, renderer=pydoc.plaintext)
|
288
|
+
|
289
|
+
|
290
|
+
def format_dimension(code, dim):
|
291
|
+
return (
|
292
|
+
f"| `{ code }` "
|
293
|
+
f'| { f" x ".join([str(d) for d in dim.values() if isinstance(d, float)]) } '
|
294
|
+
f'| { f" x ".join([k for k in dim.keys() if isinstance(dim[k], float)]) }'
|
295
|
+
)
|
296
|
+
|
297
|
+
|
298
|
+
def instantiate_tree(cls, indent=0, alias=""):
|
299
|
+
tree = f"{alias}{cls.__name__}(\n"
|
300
|
+
indent += 1
|
301
|
+
items = cls.__annotations__.items() if hasattr(cls, "__annotations__") else []
|
302
|
+
|
303
|
+
for name, typ in items:
|
304
|
+
if typ.__name__ == "Optional" and hasattr(typ, "__args__"):
|
305
|
+
typ = typ.__args__[0]
|
306
|
+
if typ.__name__ == "List" and hasattr(typ, "__args__"):
|
307
|
+
typ = typ.__args__[0]
|
308
|
+
if hasattr(typ, "__annotations__"):
|
309
|
+
tree += (
|
310
|
+
" " * indent * 4
|
311
|
+
+ f"{name}=[\n"
|
312
|
+
+ " " * (indent + 1) * 4
|
313
|
+
+ f"{instantiate_tree(typ, indent + 1, alias=alias)}\n"
|
314
|
+
+ " " * indent * 4
|
315
|
+
+ "],\n"
|
316
|
+
)
|
317
|
+
else:
|
318
|
+
tree += " " * indent * 4 + f"{name}=[],\n"
|
319
|
+
elif hasattr(typ, "__annotations__"):
|
320
|
+
tree += (
|
321
|
+
" " * indent * 4
|
322
|
+
+ f"{name}={instantiate_tree(typ, indent, alias=alias)},\n"
|
323
|
+
)
|
324
|
+
else:
|
325
|
+
tree += " " * indent * 4 + f"{name}=None,\n"
|
326
|
+
|
327
|
+
tree += " " * (indent - 1) * 4 + ")"
|
328
|
+
return tree
|
329
|
+
|
330
|
+
|
331
|
+
def instantiate_class_from_module(
|
332
|
+
module_name: str,
|
333
|
+
class_name: str,
|
334
|
+
module_alias: str = "",
|
335
|
+
):
|
336
|
+
module = importlib.import_module(module_name)
|
337
|
+
cls = getattr(module, class_name)
|
338
|
+
alias = f"{module_alias}." if module_alias != "" else ""
|
339
|
+
|
340
|
+
return instantiate_tree(cls, alias=alias)
|
341
|
+
|
342
|
+
|
343
|
+
def parse_nested_properties(properties: list[str]) -> dict:
|
344
|
+
"""
|
345
|
+
Parse a list of nested properties into a dictionary.
|
346
|
+
|
347
|
+
Example input: ["payment[paid_by]=sender", "metadata[key1]=value1"]
|
348
|
+
"""
|
349
|
+
result = {}
|
350
|
+
|
351
|
+
def set_nested(d, path, value):
|
352
|
+
keys = path.split("[")
|
353
|
+
for i, key in enumerate(keys):
|
354
|
+
key = key.rstrip("]")
|
355
|
+
if i == len(keys) - 1:
|
356
|
+
d[key] = value
|
357
|
+
else:
|
358
|
+
d = d.setdefault(key, {})
|
359
|
+
|
360
|
+
for prop in properties:
|
361
|
+
if "=" not in prop:
|
362
|
+
raise ValueError(
|
363
|
+
f"Invalid property format: {prop}. Use 'key=value' or 'nested[key]=value'."
|
364
|
+
)
|
365
|
+
path, value = prop.split("=", 1)
|
366
|
+
set_nested(result, path, value)
|
367
|
+
|
368
|
+
return result
|
File without changes
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import typer
|
2
|
+
import karrio_cli.common.utils as utils
|
3
|
+
import typing
|
4
|
+
|
5
|
+
app = typer.Typer()
|
6
|
+
|
7
|
+
@app.command("list")
|
8
|
+
def list_carriers(
|
9
|
+
pretty: bool = typer.Option(False, "--pretty", "-p", help="Pretty print the output"),
|
10
|
+
line_numbers: bool = typer.Option(False, "--line-numbers", "-n", help="Show line numbers in pretty print"),
|
11
|
+
):
|
12
|
+
"""
|
13
|
+
List all carriers.
|
14
|
+
|
15
|
+
Examples:
|
16
|
+
```terminal
|
17
|
+
# Get all carriers and display as a table
|
18
|
+
kcli carriers list | jq -r ".results[] | [.name, .display_name, .capabilities[]] | @tsv" | column -t -s $"\t"
|
19
|
+
```
|
20
|
+
|
21
|
+
```terminal
|
22
|
+
# Get carriers and extract specific fields
|
23
|
+
kcli carriers list | jq ".results[] | {name, display_name, capabilities}"
|
24
|
+
```
|
25
|
+
|
26
|
+
Example Output:
|
27
|
+
```json
|
28
|
+
{
|
29
|
+
"count": 3,
|
30
|
+
"next": null,
|
31
|
+
"previous": null,
|
32
|
+
"results": [
|
33
|
+
{
|
34
|
+
"name": "ups",
|
35
|
+
"display_name": "UPS",
|
36
|
+
"capabilities": ["rating", "shipping", "tracking"],
|
37
|
+
"services": {
|
38
|
+
"ups_standard": "11",
|
39
|
+
"ups_express": "01",
|
40
|
+
"ups_expedited": "02"
|
41
|
+
},
|
42
|
+
"requirements": ["api_key", "password", "account_number"]
|
43
|
+
}
|
44
|
+
]
|
45
|
+
}
|
46
|
+
```
|
47
|
+
"""
|
48
|
+
utils.make_get_request(
|
49
|
+
"v1/carriers", params=None, pretty_print=pretty, line_numbers=line_numbers
|
50
|
+
)
|
51
|
+
|
52
|
+
@app.command("retrieve")
|
53
|
+
def retrieve_carrier(
|
54
|
+
carrier_name: str,
|
55
|
+
pretty: bool = typer.Option(False, "--pretty", "-p", help="Pretty print the output"),
|
56
|
+
line_numbers: bool = typer.Option(False, "--line-numbers", "-n", help="Show line numbers in pretty print"),
|
57
|
+
):
|
58
|
+
"""
|
59
|
+
Retrieve a carrier by name.
|
60
|
+
|
61
|
+
Example:
|
62
|
+
```terminal
|
63
|
+
kcli carriers retrieve ups | jq "{name, display_name, capabilities, services: .services | length}"
|
64
|
+
```
|
65
|
+
|
66
|
+
Example Output:
|
67
|
+
```json
|
68
|
+
{
|
69
|
+
"name": "ups",
|
70
|
+
"display_name": "UPS",
|
71
|
+
"capabilities": ["rating", "shipping", "tracking"],
|
72
|
+
"services": {
|
73
|
+
"ups_standard": "11",
|
74
|
+
"ups_express": "01",
|
75
|
+
"ups_expedited": "02",
|
76
|
+
"ups_express_plus": "14",
|
77
|
+
"ups_worldwide_express": "07",
|
78
|
+
"ups_worldwide_expedited": "08",
|
79
|
+
"ups_standard_international": "65"
|
80
|
+
},
|
81
|
+
"requirements": ["api_key", "password", "account_number"],
|
82
|
+
"metadata": {
|
83
|
+
"test_mode_supported": true,
|
84
|
+
"multi_piece_supported": true
|
85
|
+
}
|
86
|
+
}
|
87
|
+
```
|
88
|
+
"""
|
89
|
+
utils.make_get_request(
|
90
|
+
f"v1/carriers/{carrier_name}", pretty_print=pretty, line_numbers=line_numbers
|
91
|
+
)
|