Skip to content

Commit ceb1830

Browse files
authored
🐛 Improve error message when receiving a 403 (#189)
1 parent e24a4e5 commit ceb1830

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

src/fastapi_cloud_cli/utils/api.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ def _handle_unauthorized(auth_mode: AuthMode) -> str:
209209
return message
210210

211211

212+
def _get_response_error_message(response: httpx.Response) -> str | None:
213+
try:
214+
data = response.json()
215+
except (json.JSONDecodeError, httpx.ResponseNotRead):
216+
return None
217+
218+
if not isinstance(data, dict):
219+
return None # pragma: no cover
220+
221+
detail = data.get("detail")
222+
if not isinstance(detail, str):
223+
return None # pragma: no cover
224+
225+
return detail
226+
227+
212228
def handle_http_error(
213229
error: httpx.HTTPError,
214230
default_message: str | None = None,
@@ -227,7 +243,10 @@ def handle_http_error(
227243
message = _handle_unauthorized(auth_mode=auth_mode)
228244

229245
elif status_code == 403:
230-
message = "You don't have permissions for this resource"
246+
message = (
247+
_get_response_error_message(error.response)
248+
or "You don't have permissions for this resource"
249+
)
231250

232251
if not message:
233252
message = (

tests/test_cli_deploy.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,35 @@ def test_creates_app_on_backend(
461461
assert "App created successfully" in result.output
462462

463463

464+
@pytest.mark.respx
465+
def test_shows_api_message_when_create_app_is_forbidden(
466+
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
467+
) -> None:
468+
steps = [Keys.ENTER, Keys.ENTER, *"demo", Keys.ENTER, Keys.ENTER, Keys.ENTER]
469+
team = _get_random_team()
470+
471+
respx_mock.get("/teams/").mock(return_value=Response(200, json={"data": [team]}))
472+
respx_mock.post(
473+
"/apps/", json={"name": "demo", "team_id": team["id"], "directory": None}
474+
).mock(
475+
return_value=Response(
476+
403,
477+
json={"detail": "App limit reached"},
478+
)
479+
)
480+
481+
with (
482+
changing_dir(tmp_path),
483+
patch("rich_toolkit.container.getchar") as mock_getchar,
484+
):
485+
mock_getchar.side_effect = steps
486+
487+
result = runner.invoke(app, ["deploy"])
488+
489+
assert result.exit_code == 1
490+
assert "App limit reached" in result.output
491+
492+
464493
@pytest.mark.respx
465494
def test_creates_app_with_directory(
466495
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter

0 commit comments

Comments
 (0)