diff --git a/src/globus_sdk/client.py b/src/globus_sdk/client.py index a362cd7ea..38bdab069 100644 --- a/src/globus_sdk/client.py +++ b/src/globus_sdk/client.py @@ -304,8 +304,8 @@ def request( method=method, url=url, data=utils.PayloadWrapper._prepare(data), - query_params=query_params, - headers=rheaders, + query_params=utils.filter_missing(query_params), + headers=utils.filter_missing(rheaders), encoding=encoding, authorizer=self.authorizer, allow_redirects=allow_redirects, diff --git a/src/globus_sdk/utils.py b/src/globus_sdk/utils.py index 7ca96c458..1891558f4 100644 --- a/src/globus_sdk/utils.py +++ b/src/globus_sdk/utils.py @@ -57,6 +57,12 @@ def __repr__(self) -> str: MISSING = MissingType() +def filter_missing(data: dict[str, t.Any] | None) -> dict[str, t.Any] | None: + if data is None: + return None + return {k: v for k, v in data.items() if v is not MISSING} + + def sha256_string(s: str) -> str: return hashlib.sha256(s.encode("utf-8")).hexdigest() diff --git a/tests/functional/base_client/test_filter_missing.py b/tests/functional/base_client/test_filter_missing.py new file mode 100644 index 000000000..a90b828d6 --- /dev/null +++ b/tests/functional/base_client/test_filter_missing.py @@ -0,0 +1,57 @@ +import json +import urllib.parse + +import pytest + +from globus_sdk import utils +from globus_sdk._testing import RegisteredResponse, get_last_request, load_response + + +@pytest.fixture(autouse=True) +def setup_mock_responses(): + load_response( + RegisteredResponse( + path="https://foo.api.globus.org/bar", + json={"foo": "bar"}, + ) + ) + load_response( + RegisteredResponse( + path="https://foo.api.globus.org/bar", + method="POST", + json={"foo": "bar"}, + ) + ) + + +def test_query_params_can_filter_missing(client): + res = client.get("/bar", query_params={"foo": "bar", "baz": utils.MISSING}) + assert res.http_status == 200 + req = get_last_request() + assert req.params == {"foo": "bar"} + + +def test_headers_can_filter_missing(client): + res = client.get("/bar", headers={"foo": "bar", "baz": utils.MISSING}) + assert res.http_status == 200 + req = get_last_request() + assert req.headers["foo"] == "bar" + assert "baz" not in req.headers + + +def test_json_body_can_filter_missing(client): + res = client.post("/bar", data={"foo": "bar", "baz": utils.MISSING}) + assert res.http_status == 200 + req = get_last_request() + sent = json.loads(req.body) + assert sent == {"foo": "bar"} + + +def test_form_body_can_filter_missing(client): + res = client.post( + "/bar", data={"foo": "bar", "baz": utils.MISSING}, encoding="form" + ) + assert res.http_status == 200 + req = get_last_request() + sent = urllib.parse.parse_qs(req.body) + assert sent == {"foo": ["bar"]}