fix(open3dmodel): unwrap {success,data,error} envelope from /api/recipes
Open3DModel returns the same envelope for /api/recipes as for /api/pose; the bare-list code path stays as a fallback for older mocks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
33d66019ee
commit
cb940dc1cd
2 files changed
+30
-4
No files matched your search
@@ -59,10 +59,24 @@ class Open3DModelClient:
|
|||||||
body = r.json()
|
body = r.json()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise Open3DModelUnavailable(f"non-JSON response: {e}") from e
|
raise Open3DModelUnavailable(f"non-JSON response: {e}") from e
|
||||||
if not isinstance(body, list):
|
# Open3DModel wraps list responses in the {success, data, error} envelope
|
||||||
raise Open3DModelError("UNEXPECTED_PAYLOAD",
|
# too; older mocks may return a bare list. Accept either shape.
|
||||||
f"expected list, got {type(body).__name__}")
|
if isinstance(body, dict) and "success" in body:
|
||||||
return body
|
if not body.get("success"):
|
||||||
|
err = body.get("error") or {}
|
||||||
|
raise Open3DModelError(
|
||||||
|
str(err.get("code") or "UNKNOWN"),
|
||||||
|
str(err.get("message") or "no message"),
|
||||||
|
)
|
||||||
|
data = body.get("data")
|
||||||
|
if not isinstance(data, list):
|
||||||
|
raise Open3DModelError("UNEXPECTED_PAYLOAD",
|
||||||
|
f"expected list in data, got {type(data).__name__}")
|
||||||
|
return data
|
||||||
|
if isinstance(body, list):
|
||||||
|
return body
|
||||||
|
raise Open3DModelError("UNEXPECTED_PAYLOAD",
|
||||||
|
f"expected list or envelope, got {type(body).__name__}")
|
||||||
|
|
||||||
def estimate_pose(self, recipe_id: str, points_cv_m: np.ndarray,
|
def estimate_pose(self, recipe_id: str, points_cv_m: np.ndarray,
|
||||||
intrinsics: dict[str, Any]) -> dict[str, Any]:
|
intrinsics: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
|||||||
@@ -59,6 +59,18 @@ def test_list_recipes_happy_path():
|
|||||||
assert headers.get("X-Api-Key") == "K"
|
assert headers.get("X-Api-Key") == "K"
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_recipes_envelope_shape():
|
||||||
|
"""Open3DModel wraps /api/recipes in {success, data, error}."""
|
||||||
|
body = {"success": True,
|
||||||
|
"data": [{"id": "abc", "name": "P2", "symmetry_type": "cylindrical"}],
|
||||||
|
"error": None}
|
||||||
|
response = httpx.Response(200, json=body)
|
||||||
|
client = Open3DModelClient("http://o3d:8878", api_key="K", timeout_s=2.0)
|
||||||
|
with patch("httpx.Client.get", return_value=response):
|
||||||
|
recs = client.list_recipes()
|
||||||
|
assert recs == body["data"]
|
||||||
|
|
||||||
|
|
||||||
def test_list_recipes_connect_error_raises_unavailable():
|
def test_list_recipes_connect_error_raises_unavailable():
|
||||||
client = Open3DModelClient("http://o3d:8878", api_key="K", timeout_s=2.0)
|
client = Open3DModelClient("http://o3d:8878", api_key="K", timeout_s=2.0)
|
||||||
with patch("httpx.Client.get", side_effect=httpx.ConnectError("nope")):
|
with patch("httpx.Client.get", side_effect=httpx.ConnectError("nope")):
|
||||||
|
|||||||
Reference in new issue
Block a user