From feb86298a5ba144c415f673fcff3be3afe234bd7 Mon Sep 17 00:00:00 2001 From: eastagiletracker <310448263+eastagiletracker@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:46:14 +0700 Subject: [PATCH] Send an empty String body when a request carries no params Connection#process_request returned the params object unchanged when it was empty, so the default empty Hash reached Faraday as the request body and Net::HTTP raised NoMethodError on Hash#bytesize before any bytes were written. webhooks.delete, custom_fields.delete and broadcasts.delete could never complete, and neither could tags.update or subscribers.update when called with no changed fields. custom_fields.delete and broadcasts.delete additionally read success? off the parsed response body rather than the response itself, so they now ask for the raw response the way tags.remove_from_subscriber already does. --- lib/convertkit/connection.rb | 3 ++- lib/convertkit/resources/broadcasts.rb | 2 +- lib/convertkit/resources/custom_fields.rb | 2 +- spec/lib/convertkit/connection_spec.rb | 25 +++++++++++++++++++ .../convertkit/resources/broadcasts_spec.rb | 2 +- .../resources/custom_fields_spec.rb | 2 +- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/convertkit/connection.rb b/lib/convertkit/connection.rb index 9c78096..6c3cd69 100644 --- a/lib/convertkit/connection.rb +++ b/lib/convertkit/connection.rb @@ -53,7 +53,8 @@ def default_headers(options) def process_request(method, params) return params if method == :get - return params if params.empty? + # Faraday writes the body verbatim, so an empty payload still has to be a String. + return '' if params.empty? JSON.generate(params) end diff --git a/lib/convertkit/resources/broadcasts.rb b/lib/convertkit/resources/broadcasts.rb index fd421c8..b54bfa6 100644 --- a/lib/convertkit/resources/broadcasts.rb +++ b/lib/convertkit/resources/broadcasts.rb @@ -80,7 +80,7 @@ def update(id, options= {}) # See https://developers.convertkit.com/#destroy-a-broadcast for details. # @param id [Integer] The id of the broadcast to delete. def delete(id) - response = @client.delete("#{PATH}/#{id}") + response = @client.delete("#{PATH}/#{id}", '', true) response.success? end diff --git a/lib/convertkit/resources/custom_fields.rb b/lib/convertkit/resources/custom_fields.rb index 96acda1..19f6778 100644 --- a/lib/convertkit/resources/custom_fields.rb +++ b/lib/convertkit/resources/custom_fields.rb @@ -48,7 +48,7 @@ def update(id, label) # See https://developers.convertkit.com/#destroy-field for details # @param [Integer] id The id of the custom field to delete def delete(id) - response = @client.delete("#{PATH}/#{id}") + response = @client.delete("#{PATH}/#{id}", '', true) response.success? end diff --git a/spec/lib/convertkit/connection_spec.rb b/spec/lib/convertkit/connection_spec.rb index e63078e..d4a73f6 100644 --- a/spec/lib/convertkit/connection_spec.rb +++ b/spec/lib/convertkit/connection_spec.rb @@ -42,6 +42,13 @@ ConvertKit::Connection.new(url).post('test_path', {hash: 'request_hash'}) end + + it 'sends an empty body when no params are given' do + expect(connection).to receive(:post).with('test_path', '').and_return(response) + allow(env).to receive(:body=).with({"message" => "response_hash"}) + + ConvertKit::Connection.new(url).post('test_path') + end end describe '#post_form' do @@ -93,6 +100,17 @@ ConvertKit::Connection.new(url).delete('test_path', {hash: 'request_hash'}) end + + it 'sends an empty body when no params are given' do + expect_any_instance_of(Faraday::Connection).to receive(:delete).with('test_path').and_call_original + expect(builder).to receive(:build_response) do |connection, request| + expect(request.body).to eq('') + end.and_return(response) + + allow(env).to receive(:body=).with({"message" => "response_hash"}) + + ConvertKit::Connection.new(url).delete('test_path') + end end describe '#put' do @@ -111,6 +129,13 @@ ConvertKit::Connection.new(url).put('test_path', {hash: 'request_hash'}) end + + it 'sends an empty body when no params are given' do + expect(connection).to receive(:put).with('test_path', '').and_return(response) + allow(env).to receive(:body=).with({"message" => "response_hash"}) + + ConvertKit::Connection.new(url).put('test_path') + end end describe 'when the response is not successful' do diff --git a/spec/lib/convertkit/resources/broadcasts_spec.rb b/spec/lib/convertkit/resources/broadcasts_spec.rb index a7dfc32..4e3306b 100644 --- a/spec/lib/convertkit/resources/broadcasts_spec.rb +++ b/spec/lib/convertkit/resources/broadcasts_spec.rb @@ -150,7 +150,7 @@ let(:response) { double('response', success?: true) } it 'deletes a broadcast' do - expect(client).to receive(:delete).with('broadcasts/1').and_return(response) + expect(client).to receive(:delete).with('broadcasts/1', '', true).and_return(response) expect(broadcasts.delete(1)).to be(true) end end diff --git a/spec/lib/convertkit/resources/custom_fields_spec.rb b/spec/lib/convertkit/resources/custom_fields_spec.rb index e2b74ab..4b04ead 100644 --- a/spec/lib/convertkit/resources/custom_fields_spec.rb +++ b/spec/lib/convertkit/resources/custom_fields_spec.rb @@ -63,7 +63,7 @@ let(:response) { double('response', success?: true) } it 'deletes a custom field' do - expect(client).to receive(:delete).with('custom_fields/1').and_return(response) + expect(client).to receive(:delete).with('custom_fields/1', '', true).and_return(response) expect(custom_fields.delete(1)).to be true end end