Skip to content

Commit 3f1bfb3

Browse files
CodingCossackclaude
andcommitted
fix: preserve omitted and null JSON request bodies
An optional nullable primitive request body rendered an orphan `else:` in the generated endpoint, so the client did not compile. The JSON body macro now uses the normal property transform, drops the `json` kwarg when the serialised value is UNSET, and sends explicit None as raw `content=b"null"` because HTTPX treats `json=None` as no body. Signature requiredness, nested serialisation and declared Content-Type are unchanged. Add generated-client regressions for OpenAPI 3.0 and 3.1 nullable bodies that assert request bytes through HTTPX for omitted, UNSET, None and concrete values, and regenerate the affected endpoint golden records. Fixes #1425 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 4a2f3db commit 3f1bfb3

19 files changed

Lines changed: 503 additions & 8 deletions

File tree

end_to_end_tests/functional_tests/generated_code_execution/test_nullable_request_bodies.py

Lines changed: 401 additions & 0 deletions
Large diffs are not rendered by default.

end_to_end_tests/golden-records/escapes-client/escapes_client/api/default/misc_metadata_escapes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def _get_kwargs(
2121
}
2222

2323
_kwargs["json"] = body.to_dict()
24+
if _kwargs["json"] is None:
25+
# HTTPX treats json=None as no body; send JSON null explicitly.
26+
_kwargs["content"] = b"null"
27+
del _kwargs["json"]
2428

2529
headers["Content-Type"] = "application/json"
2630

end_to_end_tests/golden-records/escapes-client/escapes_client/api/default/non_string_example.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def _get_kwargs(
2121
}
2222

2323
_kwargs["json"] = body.to_dict()
24+
if _kwargs["json"] is None:
25+
# HTTPX treats json=None as no body; send JSON null explicitly.
26+
_kwargs["content"] = b"null"
27+
del _kwargs["json"]
2428

2529
headers["Content-Type"] = "application/json"
2630

end_to_end_tests/golden-records/escapes-client/escapes_client/api/default/property_escapes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def _get_kwargs(
2121
}
2222

2323
_kwargs["json"] = body.to_dict()
24+
if _kwargs["json"] is None:
25+
# HTTPX treats json=None as no body; send JSON null explicitly.
26+
_kwargs["content"] = b"null"
27+
del _kwargs["json"]
2428

2529
headers["Content-Type"] = "application/json"
2630

end_to_end_tests/golden-records/escapes-client/escapes_client/api/printtag_escape/with_braces_path.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ def _get_kwargs(
3333
}
3434

3535
_kwargs["json"] = body.to_dict()
36+
if _kwargs["json"] is None:
37+
# HTTPX treats json=None as no body; send JSON null explicitly.
38+
_kwargs["content"] = b"null"
39+
del _kwargs["json"]
3640

3741
headers["Content-Type"] = 'application/json; profile="https://example.com/escape" + print("uh oh") + "'
3842

end_to_end_tests/golden-records/my-test-api-client/my_test_api_client/api/bodies/json_like.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,15 @@ def _get_kwargs(
2020
"url": "/bodies/json-like",
2121
}
2222

23+
_kwargs["json"] = UNSET
2324
if not isinstance(body, Unset):
2425
_kwargs["json"] = body.to_dict()
26+
if isinstance(_kwargs["json"], Unset):
27+
del _kwargs["json"]
28+
elif _kwargs["json"] is None:
29+
# HTTPX treats json=None as no body; send JSON null explicitly.
30+
_kwargs["content"] = b"null"
31+
del _kwargs["json"]
2532

2633
headers["Content-Type"] = "application/vnd+json"
2734

end_to_end_tests/golden-records/my-test-api-client/my_test_api_client/api/bodies/optional_body.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,15 @@ def _get_kwargs(
2020
"url": "/bodies/optional",
2121
}
2222

23+
_kwargs["json"] = UNSET
2324
if not isinstance(body, Unset):
2425
_kwargs["json"] = body.to_dict()
26+
if isinstance(_kwargs["json"], Unset):
27+
del _kwargs["json"]
28+
elif _kwargs["json"] is None:
29+
# HTTPX treats json=None as no body; send JSON null explicitly.
30+
_kwargs["content"] = b"null"
31+
del _kwargs["json"]
2532

2633
headers["Content-Type"] = "application/json"
2734

end_to_end_tests/golden-records/my-test-api-client/my_test_api_client/api/bodies/post_bodies_multiple.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,15 @@ def _get_kwargs(
2323
}
2424

2525
if isinstance(body, PostBodiesMultipleJsonBody):
26+
_kwargs["json"] = UNSET
2627
if not isinstance(body, Unset):
2728
_kwargs["json"] = body.to_dict()
29+
if isinstance(_kwargs["json"], Unset):
30+
del _kwargs["json"]
31+
elif _kwargs["json"] is None:
32+
# HTTPX treats json=None as no body; send JSON null explicitly.
33+
_kwargs["content"] = b"null"
34+
del _kwargs["json"]
2835

2936
headers["Content-Type"] = "application/json"
3037
if isinstance(body, File):

end_to_end_tests/golden-records/my-test-api-client/my_test_api_client/api/bodies/refs.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,15 @@ def _get_kwargs(
2020
"url": "/bodies/refs",
2121
}
2222

23+
_kwargs["json"] = UNSET
2324
if not isinstance(body, Unset):
2425
_kwargs["json"] = body.to_dict()
26+
if isinstance(_kwargs["json"], Unset):
27+
del _kwargs["json"]
28+
elif _kwargs["json"] is None:
29+
# HTTPX treats json=None as no body; send JSON null explicitly.
30+
_kwargs["content"] = b"null"
31+
del _kwargs["json"]
2532

2633
headers["Content-Type"] = "application/json"
2734

end_to_end_tests/golden-records/my-test-api-client/my_test_api_client/api/config/content_type_override.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ def _get_kwargs(
1919
"url": "/config/content-type-override",
2020
}
2121

22-
if not isinstance(body, Unset):
23-
_kwargs["json"] = body
22+
_kwargs["json"] = body
23+
if isinstance(_kwargs["json"], Unset):
24+
del _kwargs["json"]
25+
elif _kwargs["json"] is None:
26+
# HTTPX treats json=None as no body; send JSON null explicitly.
27+
_kwargs["content"] = b"null"
28+
del _kwargs["json"]
2429

2530
headers["Content-Type"] = "openapi/python/client"
2631

0 commit comments

Comments
 (0)