ktr-cli 0.1.0__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.
@@ -0,0 +1,86 @@
1
+ from typing import Any, Callable
2
+
3
+ from kantree_cli.core.errors import ValidationError
4
+
5
+ JsonObject = dict[str, Any]
6
+
7
+
8
+ def extract_object_list(
9
+ payload: Any,
10
+ *,
11
+ context: str,
12
+ preferred_keys: tuple[str, ...],
13
+ ) -> list[JsonObject]:
14
+ if isinstance(payload, list):
15
+ return normalize_object_list(payload, context=context)
16
+
17
+ if isinstance(payload, dict):
18
+ for key in preferred_keys:
19
+ maybe_list = payload.get(key)
20
+ if isinstance(maybe_list, list):
21
+ return normalize_object_list(maybe_list, context=context)
22
+
23
+ raise ValidationError(f"Unexpected response payload for {context}: expected a list.")
24
+
25
+
26
+ def extract_object(
27
+ payload: Any,
28
+ *,
29
+ context: str,
30
+ preferred_keys: tuple[str, ...],
31
+ require_id_field: bool = True,
32
+ object_check: Callable[[JsonObject], bool] | None = None,
33
+ ) -> JsonObject:
34
+ if isinstance(payload, dict):
35
+ if require_id_field and "id" in payload:
36
+ return payload
37
+ if object_check is not None and object_check(payload):
38
+ return payload
39
+ for key in preferred_keys:
40
+ maybe_object = payload.get(key)
41
+ if isinstance(maybe_object, dict):
42
+ return maybe_object
43
+
44
+ raise ValidationError(f"Unexpected response payload for {context}: expected an object.")
45
+
46
+
47
+ def normalize_object_list(items: list[Any], *, context: str) -> list[JsonObject]:
48
+ normalized: list[JsonObject] = []
49
+ for index, item in enumerate(items):
50
+ if not isinstance(item, dict):
51
+ raise ValidationError(f"Unexpected item type at index {index} for {context}.")
52
+ normalized.append(item)
53
+ return normalized
54
+
55
+
56
+ def next_page_header_value(headers: dict[str, str]) -> int | None:
57
+ raw_next_page = headers.get("X-Kantree-NextPage")
58
+ if raw_next_page is None:
59
+ raw_next_page = headers.get("x-kantree-nextpage")
60
+ if raw_next_page is None:
61
+ return None
62
+
63
+ normalized = raw_next_page.strip()
64
+ if not normalized:
65
+ return None
66
+
67
+ parsed = _parse_int(normalized)
68
+ if parsed is None or parsed < 1:
69
+ return None
70
+ return parsed
71
+
72
+
73
+ def _parse_int(value: Any) -> int | None:
74
+ if isinstance(value, bool):
75
+ return None
76
+ if isinstance(value, int):
77
+ return value
78
+ if isinstance(value, str):
79
+ stripped = value.strip()
80
+ if not stripped:
81
+ return None
82
+ try:
83
+ return int(stripped)
84
+ except ValueError:
85
+ return None
86
+ return None
@@ -0,0 +1 @@
1
+ """Service helpers for command-level workflows."""