Skip to content

Commit 2440b66

Browse files
authored
Merge pull request #248 from MerginMaps/fix-error-handling-_do_request
Fix error handling _do_request()
2 parents ddb1aee + 4be1454 commit 2440b66

File tree

2 files changed

+45
-17
lines changed

2 files changed

+45
-17
lines changed

mergin/client.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -228,25 +228,27 @@ def _do_request(self, request):
228228
try:
229229
return self.opener.open(request)
230230
except urllib.error.HTTPError as e:
231-
server_response = json.load(e)
232231

233232
err_detail = None
233+
server_response = None
234234
server_code = None
235-
# Try to get error detail
236-
if isinstance(server_response, dict):
237-
server_code = server_response.get("code")
238-
err_detail = server_response.get("detail")
239-
if not err_detail:
240-
# Extract all field-specific errors and format them
241-
err_detail = "\n".join(
242-
f"{key}: {', '.join(map(str, value))}"
243-
for key, value in server_response.items()
244-
if isinstance(value, list)
245-
) or str(
246-
server_response
247-
) # Fallback to raw response if structure is unexpected
248-
else:
249-
err_detail = str(server_response)
235+
236+
if e.fp:
237+
server_response = e.fp.read().decode("utf-8")
238+
if (
239+
e.headers.get("Content-Type", "") == "application/problem+json"
240+
or e.headers.get("Content-Type", "") == "application/json"
241+
):
242+
json_response = json.loads(server_response)
243+
if isinstance(json_response, dict):
244+
err_detail = json_response.get(
245+
"detail", None
246+
) # `detail` should be present in MM server response
247+
server_code = json_response.get("code", None)
248+
if err_detail is None:
249+
err_detail = server_response
250+
else:
251+
err_detail = server_response
250252

251253
raise ClientError(
252254
detail=err_detail,
@@ -256,6 +258,7 @@ def _do_request(self, request):
256258
http_error=e.code,
257259
http_method=request.get_method(),
258260
)
261+
259262
except urllib.error.URLError as e:
260263
# e.g. when DNS resolution fails (no internet connection?)
261264
raise ClientError("Error requesting " + request.full_url + ": " + str(e))

mergin/test/test_client.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
decode_token_data,
2121
TokenError,
2222
ServerType,
23+
WorkspaceRole,
2324
)
2425
from ..client_push import push_project_async, push_project_cancel
2526
from ..client_pull import (
@@ -2884,5 +2885,29 @@ def test_mc_without_login():
28842885
assert config["server_configured"]
28852886

28862887
# without login should not be able to access workspaces
2887-
with pytest.raises(ClientError, match="Authentication information is missing or invalid."):
2888+
with pytest.raises(ClientError) as e:
28882889
mc.workspaces_list()
2890+
2891+
assert e.value.http_error == 401
2892+
assert e.value.detail == '"Authentication information is missing or invalid."\n'
2893+
2894+
2895+
def test_do_request_error_handling(mc: MerginClient):
2896+
2897+
with pytest.raises(ClientError) as e:
2898+
mc.get("/v2/sso/connections?email=bad@email.com")
2899+
2900+
assert e.value.http_error == 404
2901+
assert (
2902+
e.value.detail
2903+
== "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again."
2904+
)
2905+
assert ": 404," in e.value.server_response
2906+
2907+
workspaces = mc.workspaces_list()
2908+
2909+
with pytest.raises(ClientError) as e:
2910+
mc.create_user("test@email.com", "123", workspace_id=workspaces[0]["id"], workspace_role=WorkspaceRole.GUEST)
2911+
2912+
assert e.value.http_error == 400
2913+
assert "Passwords must be at least 8 characters long." in e.value.detail

0 commit comments

Comments
 (0)