allowing nested params also when posting multipart requests

This commit is contained in:
HoneyryderChuck 2020-12-10 02:01:08 +00:00
parent 0bb18ad500
commit 65f5e86f3f
9 changed files with 72 additions and 34 deletions

View File

@ -11,7 +11,7 @@ Metrics/ModuleLength:
Max: 325
Metrics/BlockLength:
Max: 100
Max: 200
Metrics/BlockNesting:
Enabled: False

View File

@ -23,12 +23,26 @@ module HTTPX
def_delegator :@raw, :read
def initialize(form)
@raw = HTTP::FormData.create(form)
@raw = if multipart?(form)
HTTP::FormData::Multipart.new(Hash[*form.map { |k, v| Transcoder.enum_for(:normalize_keys, k, v).to_a }])
else
HTTP::FormData::Urlencoded.new(form, :encoder => Transcoder::Form.method(:encode))
end
end
def bytesize
@raw.content_length
end
private
def multipart?(data)
data.any? do |_, v|
v.is_a?(HTTP::FormData::Part) ||
(v.respond_to?(:to_ary) && v.to_ary.any? { |e| e.is_a?(HTTP::FormData::Part) }) ||
(v.respond_to?(:to_hash) && v.to_hash.any? { |_, e| e.is_a?(HTTP::FormData::Part) })
end
end
end
def encode(form)

View File

@ -3,6 +3,24 @@
module HTTPX
module Transcoder
extend Registry
def self.normalize_keys(key, value, &block)
if value.respond_to?(:to_ary)
if value.empty?
block.call("#{key}[]")
else
value.to_ary.each do |element|
normalize_keys("#{key}[]", element, &block)
end
end
elsif value.respond_to?(:to_hash)
value.to_hash.each do |child_key, child_value|
normalize_keys("#{key}[#{child_key}]", child_value, &block)
end
else
block.call(key.to_s, value)
end
end
end
end

View File

@ -12,13 +12,16 @@ module HTTPX::Transcoder
def_delegator :@raw, :to_s
def_delegator :@raw, :to_str
def_delegator :@raw, :bytesize
def initialize(form)
@raw = form.each_with_object("".b) do |(key, val), buf|
normalize_keys(key, val) do |k, v|
HTTPX::Transcoder.normalize_keys(key, val) do |k, v|
buf << "&" unless buf.empty?
buf << "#{URI.encode_www_form_component(k)}=#{URI.encode_www_form_component(v.to_s)}"
buf << URI.encode_www_form_component(k)
buf << "=#{URI.encode_www_form_component(v.to_s)}" unless v.nil?
end
end
end
@ -26,26 +29,6 @@ module HTTPX::Transcoder
def content_type
"application/x-www-form-urlencoded"
end
private
def normalize_keys(key, value, &block)
if value.respond_to?(:to_ary)
if value.empty?
block.call("#{key}[]", "")
else
value.to_ary.each do |element|
normalize_keys("#{key}[]", element, &block)
end
end
elsif value.respond_to?(:to_hash)
value.to_hash.each do |child_key, child_value|
normalize_keys("#{key}[#{child_key}]", child_value, &block)
end
else
block.call(key.to_s, value)
end
end
end
def encode(form)

View File

@ -12,7 +12,9 @@ module HTTPX
private
def initalize: (Hash[Symbol | string, HTTP::FormData::Part | string] multipart_data) -> untyped
def initialize: (Hash[Symbol | string, HTTP::FormData::Part | string] multipart_data) -> untyped
def multipart?: (top) -> bool
end
end
end

View File

@ -4,6 +4,9 @@ module HTTPX
module Transcoder
extend HTTPX::Registry[String, Class]
def self.normalize_keys: (string | Symbol, top) { (string, top) -> void } -> void
| (string | Symbol, top) { (string) -> void } -> void
interface _Encoder
def bytesize: () -> Numeric
end

View File

@ -11,8 +11,6 @@ module HTTPX::Transcoder
private
def initialize: (urlencoded_input form) -> untyped
def normalize_keys: (string, untyped) { (string, untyped) -> void } -> void
end
end
end

View File

@ -16,6 +16,16 @@ module Requests
verify_uploaded(body, "form", "foo" => "bar")
end
define_method :"test_plugin_multipart_nested_urlencoded_#{meth}" do
uri = build_uri("/#{meth}")
response = HTTPX.plugin(:multipart)
.send(meth, uri, form: { "q" => { "a" => "z" }, "a" => %w[1 2] })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "application/x-www-form-urlencoded")
verify_uploaded(body, "form", "q[a]" => "z", "a[]" => %w[1 2])
end
define_method :"test_plugin_multipart_formdata_#{meth}" do
uri = build_uri("/#{meth}")
response = HTTPX.plugin(:multipart)
@ -25,6 +35,16 @@ module Requests
verify_header(body["headers"], "Content-Type", "multipart/form-data")
verify_uploaded_image(body)
end
define_method :"test_plugin_multipart_nested_formdata_#{meth}" do
uri = build_uri("/#{meth}")
response = HTTPX.plugin(:multipart)
.send(meth, uri, form: { q: { image: HTTP::FormData::File.new(fixture_file_path) } })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "multipart/form-data")
verify_uploaded_image(body, "q[image]")
end
end
def fixture
@ -39,9 +59,9 @@ module Requests
File.join("test", "support", "fixtures", "image.jpg")
end
def verify_uploaded_image(body)
def verify_uploaded_image(body, key = "image")
assert body.key?("files"), "there were no files uploaded"
assert body["files"].key?("image"), "there is no image in the file"
assert body["files"].key?(key), "there is no image in the file"
end
end
end

View File

@ -14,11 +14,11 @@ module Requests
define_method :"test_#{meth}_query_nested_params" do
uri = build_uri("/#{meth}")
response = HTTPX.send(meth, uri, params: { "q" => { "a" => "z" }, "a" => %w[1 2] })
response = HTTPX.send(meth, uri, params: { "q" => { "a" => "z" }, "a" => %w[1 2], "b" => [] })
verify_status(response, 200)
body = json_body(response)
verify_uploaded(body, "args", "q[a]" => "z", "a[]" => %w[1 2])
verify_uploaded(body, "url", build_uri("/#{meth}?q[a]=z&a[]=1&a[]=2"))
verify_uploaded(body, "args", "q[a]" => "z", "a[]" => %w[1 2], "b[]" => "")
verify_uploaded(body, "url", build_uri("/#{meth}?q[a]=z&a[]=1&a[]=2&b[]"))
end
define_method :"test_#{meth}_form_params" do
@ -32,11 +32,11 @@ module Requests
define_method :"test_#{meth}_form_nested_params" do
uri = build_uri("/#{meth}")
response = HTTPX.send(meth, uri, form: { "q" => { "a" => "z" }, "a" => %w[1 2] })
response = HTTPX.send(meth, uri, form: { "q" => { "a" => "z" }, "a" => %w[1 2], "b" => [] })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "application/x-www-form-urlencoded")
verify_uploaded(body, "form", "q[a]" => "z", "a[]" => %w[1 2])
verify_uploaded(body, "form", "q[a]" => "z", "a[]" => %w[1 2], "b[]" => "")
end
define_method :"test_#{meth}_expect_100_form_params" do