diff --git a/src/fastapi_cloud_cli/utils/api.py b/src/fastapi_cloud_cli/utils/api.py index 12ef45e..2d71a1c 100644 --- a/src/fastapi_cloud_cli/utils/api.py +++ b/src/fastapi_cloud_cli/utils/api.py @@ -209,6 +209,22 @@ def _handle_unauthorized(auth_mode: AuthMode) -> str: return message +def _get_response_error_message(response: httpx.Response) -> str | None: + try: + data = response.json() + except (json.JSONDecodeError, httpx.ResponseNotRead): + return None + + if not isinstance(data, dict): + return None # pragma: no cover + + detail = data.get("detail") + if not isinstance(detail, str): + return None # pragma: no cover + + return detail + + def handle_http_error( error: httpx.HTTPError, default_message: str | None = None, @@ -227,7 +243,10 @@ def handle_http_error( message = _handle_unauthorized(auth_mode=auth_mode) elif status_code == 403: - message = "You don't have permissions for this resource" + message = ( + _get_response_error_message(error.response) + or "You don't have permissions for this resource" + ) if not message: message = ( diff --git a/tests/test_cli_deploy.py b/tests/test_cli_deploy.py index 07fdbc1..6588d60 100644 --- a/tests/test_cli_deploy.py +++ b/tests/test_cli_deploy.py @@ -461,6 +461,35 @@ def test_creates_app_on_backend( assert "App created successfully" in result.output +@pytest.mark.respx +def test_shows_api_message_when_create_app_is_forbidden( + logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter +) -> None: + steps = [Keys.ENTER, Keys.ENTER, *"demo", Keys.ENTER, Keys.ENTER, Keys.ENTER] + team = _get_random_team() + + respx_mock.get("/teams/").mock(return_value=Response(200, json={"data": [team]})) + respx_mock.post( + "/apps/", json={"name": "demo", "team_id": team["id"], "directory": None} + ).mock( + return_value=Response( + 403, + json={"detail": "App limit reached"}, + ) + ) + + with ( + changing_dir(tmp_path), + patch("rich_toolkit.container.getchar") as mock_getchar, + ): + mock_getchar.side_effect = steps + + result = runner.invoke(app, ["deploy"]) + + assert result.exit_code == 1 + assert "App limit reached" in result.output + + @pytest.mark.respx def test_creates_app_with_directory( logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter