From 787f9ef100d27d11521d15e6bc2d37718e45981e Mon Sep 17 00:00:00 2001 From: dkinder Date: Fri, 24 Apr 2026 17:02:37 -0400 Subject: [PATCH 1/3] client: allow nil body in DoJSON --- client.go | 8 ++++++++ client_test.go | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/client.go b/client.go index 03aff7b..c45f258 100644 --- a/client.go +++ b/client.go @@ -61,6 +61,10 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { // JSON // +// DoJSON, GetJSON, PostJSON, PutJSON, and DeleteJSON all set the appropriate +// JSON headers and decode the response body into the target. Pass nil as +// target to skip decoding — useful for endpoints that return no body (e.g. +// 204 No Content). func (c *Client) DoJSON(req *http.Request, target any) error { req.Header.Set("Content-Type", "application/json") @@ -84,6 +88,10 @@ func (c *Client) DoJSON(req *http.Request, target any) error { return fmt.Errorf("got %d code and response: %s", resp.StatusCode, string(body)) } + if target == nil { + return nil + } + if err := json.NewDecoder(resp.Body).Decode(&target); err != nil { return fmt.Errorf("got %d code and failed to decode response body: %w", resp.StatusCode, err) } diff --git a/client_test.go b/client_test.go index 0da6ff8..c6a3387 100644 --- a/client_test.go +++ b/client_test.go @@ -71,6 +71,19 @@ func TestClientDoJSON(t *testing.T) { assert.Equal(t, "joebob", user.Name) } +func TestClientDoJSONNilTarget204(t *testing.T) { + handler, client, cleanup := setup(t) + defer cleanup() + + handler.On("HandleWithHeaders", "DELETE", "/users/1", jsonHeaderMatcher, mock.Anything).Return(httpmock.Response{ + Status: http.StatusNoContent, + }) + + req, err := http.NewRequest("DELETE", "/users/1", nil) + require.NoError(t, err) + require.NoError(t, client.DoJSON(req, nil)) +} + type TestUser struct { ID int `json:"id,omitempty"` Name string `json:"name"` From f2da16bb962e51078f89c8274f9e604bfcd85904 Mon Sep 17 00:00:00 2001 From: Dan Kinder Date: Fri, 24 Apr 2026 17:08:05 -0400 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client.go b/client.go index c45f258..4d32129 100644 --- a/client.go +++ b/client.go @@ -89,6 +89,7 @@ func (c *Client) DoJSON(req *http.Request, target any) error { } if target == nil { + _, _ = io.Copy(io.Discard, resp.Body) return nil } From 65df3544b6806c86653c374db540c3ca84d000b8 Mon Sep 17 00:00:00 2001 From: dkinder Date: Fri, 24 Apr 2026 17:08:33 -0400 Subject: [PATCH 3/3] Decode JSON without extra indirection Since target is already an interface, no need for more indirection, it could cause unexpected behavior. --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 4d32129..afd1557 100644 --- a/client.go +++ b/client.go @@ -93,7 +93,7 @@ func (c *Client) DoJSON(req *http.Request, target any) error { return nil } - if err := json.NewDecoder(resp.Body).Decode(&target); err != nil { + if err := json.NewDecoder(resp.Body).Decode(target); err != nil { return fmt.Errorf("got %d code and failed to decode response body: %w", resp.StatusCode, err) } return nil