robot-wrapper-sdk 0.2.6__tar.gz → 0.2.7__tar.gz

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 (19) hide show
  1. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/PKG-INFO +1 -1
  2. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/pyproject.toml +1 -1
  3. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/infrastructure/api_client.py +25 -1
  4. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/infrastructure/robot_api_repository.py +33 -8
  5. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_wrapper_sdk.egg-info/PKG-INFO +1 -1
  6. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/README.md +0 -0
  7. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/examples/main.py +0 -0
  8. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/__init__.py +0 -0
  9. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/application/__init__.py +0 -0
  10. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/application/usecases.py +0 -0
  11. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/domain/__init__.py +0 -0
  12. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/domain/entities.py +0 -0
  13. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/domain/repositories.py +0 -0
  14. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_sdk/infrastructure/__init__.py +0 -0
  15. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_wrapper_sdk.egg-info/SOURCES.txt +0 -0
  16. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_wrapper_sdk.egg-info/dependency_links.txt +0 -0
  17. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_wrapper_sdk.egg-info/requires.txt +0 -0
  18. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/robot_wrapper_sdk.egg-info/top_level.txt +0 -0
  19. {robot_wrapper_sdk-0.2.6 → robot_wrapper_sdk-0.2.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robot-wrapper-sdk
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Robot Platform API SDK
5
5
  Author-email: GH Robot Platform Team <team@ghrobot.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
 
9
9
  [project]
10
10
  name = "robot-wrapper-sdk"
11
- version = "0.2.6"
11
+ version = "0.2.7"
12
12
  description = "Robot Platform API SDK"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.10"
@@ -1,6 +1,7 @@
1
1
  import httpx
2
2
  import os
3
3
  from typing import Dict, Any, Optional
4
+ from httpx import RemoteProtocolError
4
5
 
5
6
  def _extract_tokens(payload: Dict[str, Any]) -> tuple[Optional[str], Optional[str]]:
6
7
  data = payload.get("data") if isinstance(payload, dict) else None
@@ -132,7 +133,30 @@ class HTTPClient:
132
133
  full_path = f"/api/v1/developer{path}"
133
134
 
134
135
  print(f"🚀 Requesting: {self.base_url}{full_path}")
135
- resp = self.client.request(method, full_path, **kwargs)
136
+ last_err: Optional[Exception] = None
137
+ resp = None
138
+ for attempt in range(2):
139
+ try:
140
+ resp = self.client.request(method, full_path, **kwargs)
141
+ break
142
+ except Exception as err:
143
+ msg = str(err)
144
+ is_disconnect = isinstance(err, RemoteProtocolError) or "Server disconnected without sending a response" in msg
145
+ if not is_disconnect or attempt == 1:
146
+ raise
147
+ last_err = err
148
+ self.client.close()
149
+ self.client = httpx.Client(
150
+ base_url=self.base_url,
151
+ auth=RobotAuth(self._get_or_login, self._refresh),
152
+ proxy=self.proxy_url,
153
+ )
154
+
155
+ if resp is None:
156
+ if last_err is not None:
157
+ raise last_err
158
+ raise RuntimeError("Request failed without response")
159
+
136
160
  resp.raise_for_status()
137
161
  try:
138
162
  return dict(resp.json())
@@ -3,6 +3,33 @@ from .api_client import HTTPClient, AsyncHTTPClient
3
3
  from ..domain.repositories import RobotRepository, AsyncRobotRepository
4
4
  from ..domain.entities import Robot, RobotSecrets
5
5
 
6
+
7
+ def _extract_list_payload(resp: Dict[str, Any]) -> Tuple[List[Dict[str, Any]], int]:
8
+ container = resp.get("data") if isinstance(resp, dict) else None
9
+ if isinstance(container, dict):
10
+ items = container.get("data")
11
+ total = container.get("total", 0)
12
+ else:
13
+ items = container
14
+ total = resp.get("total", 0) if isinstance(resp, dict) else 0
15
+
16
+ if not isinstance(items, list):
17
+ return [], int(total) if isinstance(total, int) else 0
18
+
19
+ mapped = [item for item in items if isinstance(item, dict)]
20
+ return mapped, int(total) if isinstance(total, int) else 0
21
+
22
+
23
+ def _extract_items(resp: Dict[str, Any]) -> List[Dict[str, Any]]:
24
+ container = resp.get("data") if isinstance(resp, dict) else None
25
+ if isinstance(container, list):
26
+ return [item for item in container if isinstance(item, dict)]
27
+ if isinstance(container, dict):
28
+ nested = container.get("data")
29
+ if isinstance(nested, list):
30
+ return [item for item in nested if isinstance(item, dict)]
31
+ return []
32
+
6
33
  class RobotAPIRepository(RobotRepository):
7
34
  def __init__(self, client: HTTPClient):
8
35
  self.client = client
@@ -28,8 +55,7 @@ class RobotAPIRepository(RobotRepository):
28
55
  if project_id: params["project_id"] = project_id
29
56
 
30
57
  resp = self.client.request("GET", "/robots", params=params)
31
- data = resp.get("data", [])
32
- total = resp.get("total", 0)
58
+ data, total = _extract_list_payload(resp)
33
59
  return [Robot(**r) for r in data], total
34
60
 
35
61
  def acquire_need_login(self, limit: int = 20, lock_minutes: int = 30) -> List[Robot]:
@@ -38,7 +64,7 @@ class RobotAPIRepository(RobotRepository):
38
64
  "/robots/login/acquire",
39
65
  json={"limit": limit, "lock_minutes": lock_minutes},
40
66
  )
41
- data = resp.get("data", [])
67
+ data = _extract_items(resp)
42
68
  return [Robot(**r) for r in data]
43
69
 
44
70
  def acquire_unhardened(self, limit: int = 20, lock_minutes: int = 30, min_age_days: int = 0) -> List[Robot]:
@@ -47,7 +73,7 @@ class RobotAPIRepository(RobotRepository):
47
73
  "/robots/security-hardened/acquire",
48
74
  json={"limit": limit, "lock_minutes": lock_minutes, "min_age_days": min_age_days},
49
75
  )
50
- data = resp.get("data", [])
76
+ data = _extract_items(resp)
51
77
  return [Robot(**r) for r in data]
52
78
 
53
79
  def update_auth_status(self, robot_id: str, auth_status: str) -> None:
@@ -89,8 +115,7 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
89
115
  if project_id: params["project_id"] = project_id
90
116
 
91
117
  resp = await self.client.request("GET", "/robots", params=params)
92
- data = resp.get("data", [])
93
- total = resp.get("total", 0)
118
+ data, total = _extract_list_payload(resp)
94
119
  return [Robot(**r) for r in data], total
95
120
 
96
121
  async def acquire_need_login(self, limit: int = 20, lock_minutes: int = 30) -> List[Robot]:
@@ -99,7 +124,7 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
99
124
  "/robots/login/acquire",
100
125
  json={"limit": limit, "lock_minutes": lock_minutes},
101
126
  )
102
- data = resp.get("data", [])
127
+ data = _extract_items(resp)
103
128
  return [Robot(**r) for r in data]
104
129
 
105
130
  async def acquire_unhardened(self, limit: int = 20, lock_minutes: int = 30, min_age_days: int = 0) -> List[Robot]:
@@ -108,7 +133,7 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
108
133
  "/robots/security-hardened/acquire",
109
134
  json={"limit": limit, "lock_minutes": lock_minutes, "min_age_days": min_age_days},
110
135
  )
111
- data = resp.get("data", [])
136
+ data = _extract_items(resp)
112
137
  return [Robot(**r) for r in data]
113
138
 
114
139
  async def update_auth_status(self, robot_id: str, auth_status: str) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robot-wrapper-sdk
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Robot Platform API SDK
5
5
  Author-email: GH Robot Platform Team <team@ghrobot.com>
6
6
  License: MIT