diff --git a/qfieldcloud_sdk/sdk.py b/qfieldcloud_sdk/sdk.py index f7330aa..d3c1f83 100644 --- a/qfieldcloud_sdk/sdk.py +++ b/qfieldcloud_sdk/sdk.py @@ -2131,7 +2131,8 @@ def _request( "GET requests must not have `data` values passed, use `params` instead" ) - allow_redirects = method != "POST" + if allow_redirects is None: + allow_redirects = method != "POST" if not skip_token and self.token: headers_copy["Authorization"] = f"token {self.token}" @@ -2175,23 +2176,30 @@ def _request( if os.environ.get("ENVIRONMENT") == "test": return request.mock_response() - else: - prepared_request = self.session.prepare_request(request) - - # Merge environment settings into session - settings = self.session.merge_environment_settings( - url=prepared_request.url, - proxies={}, - stream=stream, - verify=self.verify_ssl, - cert=None, - ) - settings = settings | session_params # type: ignore - response = self.session.send( - prepared_request, - timeout=(self.connect_timeout, self.read_timeout), - **settings, + prepared_request = self.session.prepare_request(request) + + # Merge environment settings into session + settings = self.session.merge_environment_settings( + url=prepared_request.url, + proxies={}, + stream=stream, + verify=self.verify_ssl, + cert=None, + ) + settings = settings | session_params # type: ignore + + response = self.session.send( + prepared_request, + timeout=(self.connect_timeout, self.read_timeout), + **settings, + ) + + if not allow_redirects and response.is_redirect: + raise QfcRequestException( + response, + f"Unresolved redirect for {method} {response.url!r}: " + f"{response.status_code} -> {response.headers.get('Location')!r}", ) try: diff --git a/tests/test_cli_client.py b/tests/test_cli_client.py index 4f98fad..0d8b40e 100644 --- a/tests/test_cli_client.py +++ b/tests/test_cli_client.py @@ -2,10 +2,12 @@ import unittest from unittest import mock +import requests from click.testing import CliRunner +from requests.structures import CaseInsensitiveDict from qfieldcloud_sdk.cli import QFIELDCLOUD_DEFAULT_URL, cli -from qfieldcloud_sdk.sdk import Client, Pagination, QfcException +from qfieldcloud_sdk.sdk import Client, Pagination, QfcException, QfcRequestException class TestSDK(unittest.TestCase): @@ -92,6 +94,27 @@ def test_timeout_invalid_arg_value_raises(self): with self.assertRaises(QfcException): Client(QFIELDCLOUD_DEFAULT_URL, read_timeout="soon") # type: ignore + def test_request_raises_on_unresolved_post_redirect(self): + fake_response = requests.Response() + fake_response.status_code = 301 + fake_response.reason = "Moved Permanently" + fake_response.url = f"{self.client.url}files/some-project/some-file.gpkg/" + fake_response.headers = CaseInsensitiveDict( + {"Location": "http://another-address/files/some-project/some-file.gpkg/"} + ) + fake_response._content = b"" + + # We need to unset the `ENVIRONMENT` variable, because otherwise the client + # would use the mock response and we cannot test the redirect behavior. + with mock.patch.dict(os.environ, {}, clear=False): + os.environ.pop("ENVIRONMENT", None) + + with mock.patch.object( + self.client.session, "send", return_value=fake_response + ): + with self.assertRaises(QfcRequestException): + self.client._request("POST", "files/some-project/some-file.gpkg") + class TestCLI(unittest.TestCase): @classmethod